summaryrefslogtreecommitdiffstats
path: root/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'main.c')
-rw-r--r--main.c384
1 files changed, 384 insertions, 0 deletions
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..87d3ce4
--- /dev/null
+++ b/main.c
@@ -0,0 +1,384 @@
+#include <Windows.h>
+#include <emmintrin.h>
+#include <smmintrin.h>
+
+int _fltused;
+
+BOOL g_is_open = FALSE;
+LONG g_width = 640;
+LONG g_height = 480;
+void* g_pixels = NULL;
+BITMAPINFO bmp_info;
+UINT64 avg = 0U;
+UINT64 avg_fs = 0U;
+UINT64 avg_simd = 0U;
+UINT64 frame_count = 0U;
+__m128i g_zero;
+__m128i g_one;
+
+typedef struct aos_tri {
+ UINT32 p1[2];
+ UINT32 p2[2];
+ UINT32 p3[2];
+} aos_tri;
+
+typedef struct soa_tri {
+ UINT32 x[3];
+ UINT32 y[3];
+} soa_tri;
+
+aos_tri aos_triangles[] = {
+ {
+ .p1 = { 128, 384 },
+ .p2 = { 320, 96 },
+ .p3 = { 512, 384 },
+ },
+};
+soa_tri soa_triangles[] = {
+ {
+ .x = { 128, 320, 512 },
+ .y = { 384, 96, 384 },
+ },
+};
+
+void RenderFullscren(void) {
+ DWORD64 start = __rdtsc();
+
+ UINT8* pixels = (UINT8*)g_pixels;
+ aos_tri t = aos_triangles[0];
+ UINT32 max_x = max(t.p1[0], max(t.p2[0], t.p3[0]));
+ UINT32 min_x = min(t.p1[0], min(t.p2[0], t.p3[0]));
+ UINT32 max_y = max(t.p1[1], max(t.p2[1], t.p3[1]));
+ UINT32 min_y = min(t.p1[1], min(t.p2[1], t.p3[1]));
+
+ INT32 v1[2] = { t.p2[0] - t.p1[0], t.p2[1] - t.p1[1] };
+ INT32 v2[2] = { t.p3[0] - t.p2[0], t.p3[1] - t.p2[1] };
+ INT32 v3[2] = { t.p1[0] - t.p3[0], t.p1[1] - t.p3[1] };
+
+ for (INT32 i = 0; i < g_width * g_height; i++) {
+ UINT32 x = i % g_width;
+ UINT32 y = i / g_width;
+ if (min_x <= x && x <= max_x &&
+ min_y <= y && y <= max_y) {
+
+ INT32 t1[2] = { x - t.p1[0], y - t.p1[1] };
+ INT32 t2[2] = { x - t.p2[0], y - t.p2[1] };
+ INT32 t3[2] = { x - t.p3[0], y - t.p3[1] };
+
+ INT32 d_t1 = v1[0] * t1[1] - t1[0] * v1[1];
+ INT32 d_t2 = v2[0] * t2[1] - t2[0] * v2[1];
+ INT32 d_t3 = v3[0] * t3[1] - t3[0] * v3[1];
+
+ if (d_t1 > 0.f && d_t2 > 0.f && d_t3 > 0.f) {
+ pixels[i * 4] = 255;
+ }
+ }
+ }
+
+ DWORD64 end = __rdtsc();
+ DWORD64 diff = end - start;
+
+ avg_fs = ((avg_fs * frame_count) + diff) / (frame_count + 1);
+
+ char buffer[1024];
+ wsprintfA(buffer, "fullscreen scan %u cycles per triangle\n", diff);
+ OutputDebugStringA(buffer);
+ wsprintfA(buffer, "fullscreen scan %u avg cycles per triangle\n", avg_fs);
+ OutputDebugStringA(buffer);
+}
+
+void RenderTriangle(void) {
+ DWORD64 start = __rdtsc();
+
+ UINT8* pixels = (UINT8*)g_pixels;
+ aos_tri t = aos_triangles[0];
+ UINT32 max_x = max(t.p1[0], max(t.p2[0], t.p3[0]));
+ UINT32 min_x = min(t.p1[0], min(t.p2[0], t.p3[0]));
+ UINT32 max_y = max(t.p1[1], max(t.p2[1], t.p3[1]));
+ UINT32 min_y = min(t.p1[1], min(t.p2[1], t.p3[1]));
+
+ INT32 v1[2] = { t.p2[0] - t.p1[0], t.p2[1] - t.p1[1] };
+ INT32 v2[2] = { t.p3[0] - t.p2[0], t.p3[1] - t.p2[1] };
+ INT32 v3[2] = { t.p1[0] - t.p3[0], t.p1[1] - t.p3[1] };
+
+ for (UINT32 y = min_y; y < max_y + 1; y++) {
+ for (UINT32 x = min_x; x < max_x + 1; x++) {
+
+ INT32 t1[2] = { x - t.p1[0], y - t.p1[1] };
+ INT32 t2[2] = { x - t.p2[0], y - t.p2[1] };
+ INT32 t3[2] = { x - t.p3[0], y - t.p3[1] };
+
+ INT32 d_t1 = v1[0] * t1[1] - t1[0] * v1[1];
+ INT32 d_t2 = v2[0] * t2[1] - t2[0] * v2[1];
+ INT32 d_t3 = v3[0] * t3[1] - t3[0] * v3[1];
+
+ if (d_t1 > 0.f && d_t2 > 0.f && d_t3 > 0.f) {
+ pixels[((y * g_width) + x) * 4 + 1] = 255;
+ }
+ }
+ }
+
+ DWORD64 end = __rdtsc();
+ DWORD64 diff = end - start;
+
+ // avg = (avg + diff) / 2;
+ avg = ((avg * frame_count) + diff) / (frame_count + 1);
+
+ char buffer[1024];
+ wsprintfA(buffer, "%u cycles per triangle\n", diff);
+ OutputDebugStringA(buffer);
+ wsprintfA(buffer, "%u avg cycles per triangle\n", avg);
+ OutputDebugStringA(buffer);
+}
+
+void RenderTriangleSIMD(void) {
+ DWORD64 start = __rdtsc();
+
+ UINT64* pixels = (UINT64*)g_pixels;
+ soa_tri t = soa_triangles[0];
+ UINT32 max_x = max(t.x[0], max(t.x[1], t.x[2]));
+ UINT32 min_x = min(t.x[0], min(t.x[1], t.x[2]));
+ UINT32 max_y = max(t.y[0], max(t.y[1], t.y[2]));
+ UINT32 min_y = min(t.y[0], min(t.y[1], t.y[2]));
+
+ max_x += max_x % 2;
+ min_x -= min_x % 2;
+ max_y += max_y % 2;
+ min_y -= min_y % 2;
+
+ __m128i p1_x = _mm_set1_epi32(t.x[0]);
+ __m128i p1_y = _mm_set1_epi32(t.y[0]);
+ __m128i p2_x = _mm_set1_epi32(t.x[1]);
+ __m128i p2_y = _mm_set1_epi32(t.y[1]);
+ __m128i p3_x = _mm_set1_epi32(t.x[2]);
+ __m128i p3_y = _mm_set1_epi32(t.y[2]);
+
+ __m128i v1_x = _mm_set1_epi32(t.x[1] - t.x[0]);
+ __m128i v1_y = _mm_set1_epi32(t.y[1] - t.y[0]);
+ __m128i v2_x = _mm_set1_epi32(t.x[2] - t.x[1]);
+ __m128i v2_y = _mm_set1_epi32(t.y[2] - t.y[1]);
+ __m128i v3_x = _mm_set1_epi32(t.x[0] - t.x[2]);
+ __m128i v3_y = _mm_set1_epi32(t.y[0] - t.y[2]);
+
+ // min and max must be multiples of 2
+ for (UINT32 y = min_y; y < max_y + 1; y+=2) {
+ UINT32 row_1 = y * g_width;
+ UINT32 row_2 = (y + 1) * g_width;
+ for (UINT32 x = min_x; x < max_x + 1; x+=2) {
+ // work on 2x2
+ // _________
+ // | | |
+ // | 1 | 2 |
+ // |___|___|
+ // | | |
+ // | 3 | 4 |
+ // |___|___|
+ // 1
+ // {
+ // INT32 t1[2] = { x - tt.p1[0], y - tt.p1[1] };
+ // INT32 t2[2] = { x - tt.p2[0], y - tt.p2[1] };
+ // INT32 t3[2] = { x - tt.p3[0], y - tt.p3[1] };
+
+ // INT32 dt_t1 = v1[0] * t1[1] - t1[0] * v1[1];
+ // INT32 dt_t2 = v2[0] * t2[1] - t2[0] * v2[1];
+ // INT32 dt_t3 = v3[0] * t3[1] - t3[0] * v3[1];
+
+ // if (dt_t1 > 0.f && dt_t2 > 0.f && dt_t3 > 0.f) {
+ // pixels[((y * g_width) + x) * 4 + 2] = 255;
+ // }
+ // }
+ // // 2
+ // {
+ // INT32 t1[2] = { x + 1 - t.p1[0], y - t.p1[1] };
+ // INT32 t2[2] = { x + 1 - t.p2[0], y - t.p2[1] };
+ // INT32 t3[2] = { x + 1 - t.p3[0], y - t.p3[1] };
+
+ // INT32 d_t1 = v1[0] * t1[1] - t1[0] * v1[1];
+ // INT32 d_t2 = v2[0] * t2[1] - t2[0] * v2[1];
+ // INT32 d_t3 = v3[0] * t3[1] - t3[0] * v3[1];
+
+ // if (d_t1 > 0.f && d_t2 > 0.f && d_t3 > 0.f) {
+ // pixels[((y * g_width) + x + 1) * 4 + 2] = 255;
+ // }
+ // }
+ // // 3
+ // {
+ // INT32 t1[2] = { x - t.p1[0], y + 1 - t.p1[1] };
+ // INT32 t2[2] = { x - t.p2[0], y + 1 - t.p2[1] };
+ // INT32 t3[2] = { x - t.p3[0], y + 1 - t.p3[1] };
+
+ // INT32 d_t1 = v1[0] * t1[1] - t1[0] * v1[1];
+ // INT32 d_t2 = v2[0] * t2[1] - t2[0] * v2[1];
+ // INT32 d_t3 = v3[0] * t3[1] - t3[0] * v3[1];
+
+ // if (d_t1 > 0.f && d_t2 > 0.f && d_t3 > 0.f) {
+ // pixels[(((y + 1) * g_width) + x) * 4 + 2] = 255;
+ // }
+ // }
+ // // 4
+ // {
+ // INT32 t1[2] = { x + 1 - t.p1[0], y + 1 - t.p1[1] };
+ // INT32 t2[2] = { x + 1 - t.p2[0], y + 1 - t.p2[1] };
+ // INT32 t3[2] = { x + 1 - t.p3[0], y + 1 - t.p3[1] };
+
+ // INT32 d_t1 = v1[0] * t1[1] - t1[0] * v1[1];
+ // INT32 d_t2 = v2[0] * t2[1] - t2[0] * v2[1];
+ // INT32 d_t3 = v3[0] * t3[1] - t3[0] * v3[1];
+
+ // if (d_t1 > 0.f && d_t2 > 0.f && d_t3 > 0.f) {
+ // pixels[(((y + 1) * g_width) + x + 1) * 4 + 2] = 255;
+ // }
+ // }
+
+ __m128i vx = _mm_set_epi32(x + 1, x, x + 1, x);
+ __m128i vy = _mm_set_epi32(y + 1, y + 1, y, y);
+
+ __m128i p_t1x = _mm_sub_epi32(vx, p1_x);
+ __m128i p_t1y = _mm_sub_epi32(vy, p1_y);
+ __m128i p_t2x = _mm_sub_epi32(vx, p2_x);
+ __m128i p_t2y = _mm_sub_epi32(vy, p2_y);
+ __m128i p_t3x = _mm_sub_epi32(vx, p3_x);
+ __m128i p_t3y = _mm_sub_epi32(vy, p3_y);
+
+ __m128i d_t1_1 = _mm_mullo_epi32(v1_x, p_t1y);
+ __m128i d_t1_2 = _mm_mullo_epi32(v1_y, p_t1x);
+ __m128i d_t2_1 = _mm_mullo_epi32(v2_x, p_t2y);
+ __m128i d_t2_2 = _mm_mullo_epi32(v2_y, p_t2x);
+ __m128i d_t3_1 = _mm_mullo_epi32(v3_x, p_t3y);
+ __m128i d_t3_2 = _mm_mullo_epi32(v3_y, p_t3x);
+
+ __m128i d_t1 = _mm_sub_epi32(d_t1_1, d_t1_2);
+ __m128i d_t2 = _mm_sub_epi32(d_t2_1, d_t2_2);
+ __m128i d_t3 = _mm_sub_epi32(d_t3_1, d_t3_2);
+
+ __m128i t1_gtz = _mm_cmpgt_epi32(d_t1, g_zero);
+ __m128i t2_gtz = _mm_cmpgt_epi32(d_t2, g_zero);
+ __m128i t1_and_t2 = _mm_and_si128(t1_gtz, t2_gtz);
+ __m128i t3_gtz = _mm_cmpgt_epi32(d_t3, g_zero);
+
+ __m128i all_gtz = _mm_and_si128(t1_and_t2, t3_gtz);
+
+ pixels[(row_1 + x) / 2] = _mm_extract_epi64(all_gtz, 0);
+ pixels[(row_2 + x) / 2] = _mm_extract_epi64(all_gtz, 1);
+ }
+ }
+
+ DWORD64 end = __rdtsc();
+ DWORD64 diff = end - start;
+
+ // avg_simd = (avg_simd + diff) / 2;
+ avg_simd = ((avg_simd * frame_count) + diff) / (frame_count + 1);
+
+ char buffer[1024];
+ wsprintfA(buffer, "SIMD %u cycles per triangle\n", diff);
+ OutputDebugStringA(buffer);
+ wsprintfA(buffer, "SIMD %u avg cycles per triangle\n", avg_simd);
+ OutputDebugStringA(buffer);
+}
+
+void ResizeBackBuffer() {
+ if (g_pixels) {
+ VirtualFree(g_pixels, 0, MEM_RELEASE);
+ }
+
+ bmp_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmp_info.bmiHeader.biWidth = g_width;
+ bmp_info.bmiHeader.biHeight = -g_height;
+ bmp_info.bmiHeader.biPlanes = 1;
+ bmp_info.bmiHeader.biBitCount = 32;
+ bmp_info.bmiHeader.biCompression = BI_RGB;
+
+ g_pixels = VirtualAlloc(NULL, g_width * g_height * 4, MEM_COMMIT, PAGE_READWRITE);
+}
+
+LRESULT CALLBACK WndProc(HWND window, UINT msg, WPARAM wparam, LPARAM lparam) {
+ switch(msg) {
+ case WM_CREATE: {
+ g_is_open = TRUE;
+
+ RECT rect;
+ GetWindowRect(window, &rect);
+
+ g_width = rect.right - rect.left;
+ g_height = rect.bottom - rect.top;
+
+ ResizeBackBuffer();
+ break;
+ }
+ case WM_SIZE: {
+ g_width = LOWORD(lparam);
+ g_height = HIWORD(lparam);
+
+ ResizeBackBuffer();
+ break;
+ }
+ case WM_CLOSE: {
+ g_is_open = FALSE;
+ PostQuitMessage(0);
+ break;
+ }
+ case WM_DESTROY: {
+ DestroyWindow(window);
+ break;
+ }
+ case WM_PAINT: {
+ RenderFullscren();
+ RenderTriangle();
+ RenderTriangleSIMD();
+ frame_count++;
+
+ PAINTSTRUCT paint;
+ HDC hdc = BeginPaint(window, &paint);
+
+ StretchDIBits(
+ hdc,
+ 0, 0, g_width, g_height,
+ 0, 0, g_width, g_height,
+ g_pixels,
+ &bmp_info,
+ DIB_RGB_COLORS,
+ SRCCOPY
+ );
+
+ EndPaint(window, &paint);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return DefWindowProcA(window, msg, wparam, lparam);
+}
+
+int mainCRTStartup(void) {
+ const char* class_name = "sftw_rst_class";
+ WNDCLASS wnd_class = {
+ .style = CS_HREDRAW | CS_VREDRAW,
+ .lpfnWndProc = &WndProc,
+ .hInstance = NULL,
+ .lpszClassName = class_name,
+ };
+ RegisterClass(&wnd_class);
+
+ HWND wnd = CreateWindowA(class_name, "Software Rasterizer", WS_OVERLAPPEDWINDOW, 10, 10, g_width, g_height, NULL, NULL, NULL, NULL);
+
+ ShowWindow(wnd, SW_SHOW);
+
+ g_zero = _mm_set1_epi32(-1);
+ g_one = _mm_set1_epi32(255);
+
+ while(g_is_open) {
+ MSG msg;
+ while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ RenderFullscren();
+ RenderTriangle();
+ RenderTriangleSIMD();
+ frame_count++;
+ }
+
+ return 0;
+}