From 46b93ede3bdc85eb19dbac801a4e62e9c04203e4 Mon Sep 17 00:00:00 2001 From: Matt Borgerson Date: Tue, 10 Feb 2026 01:25:23 -0700 Subject: [PATCH] nv2a: Drop now-unused winding test --- hw/xbox/nv2a/pgraph/gl/gpuprops.c | 369 ----------------- hw/xbox/nv2a/pgraph/gl/meson.build | 1 - hw/xbox/nv2a/pgraph/gl/renderer.c | 8 - hw/xbox/nv2a/pgraph/gl/renderer.h | 4 - hw/xbox/nv2a/pgraph/pgraph.h | 10 - hw/xbox/nv2a/pgraph/vk/gpuprops.c | 608 ----------------------------- hw/xbox/nv2a/pgraph/vk/meson.build | 1 - hw/xbox/nv2a/pgraph/vk/renderer.c | 2 - hw/xbox/nv2a/pgraph/vk/renderer.h | 4 - 9 files changed, 1007 deletions(-) delete mode 100644 hw/xbox/nv2a/pgraph/gl/gpuprops.c delete mode 100644 hw/xbox/nv2a/pgraph/vk/gpuprops.c diff --git a/hw/xbox/nv2a/pgraph/gl/gpuprops.c b/hw/xbox/nv2a/pgraph/gl/gpuprops.c deleted file mode 100644 index 819309e50a..0000000000 --- a/hw/xbox/nv2a/pgraph/gl/gpuprops.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Geforce NV2A PGRAPH OpenGL Renderer - * - * Copyright (c) 2012 espes - * Copyright (c) 2015 Jannik Vogel - * Copyright (c) 2018-2025 Matt Borgerson - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "debug.h" -#include "renderer.h" - -static GPUProperties pgraph_gl_gpu_properties; - -static const char *vertex_shader_source = - "#version 400\n" - "out vec3 v_fragColor;\n" - "\n" - "vec2 positions[11] = vec2[](\n" - " vec2(-0.5, -0.75),\n" - " vec2(-0.25, -0.25),\n" - " vec2(-0.75, -0.25),\n" - " vec2(0.25, -0.25),\n" - " vec2(0.25, -0.75),\n" - " vec2(0.75, -0.25),\n" - " vec2(0.75, -0.75),\n" - " vec2(-0.75, 0.75),\n" - " vec2(-0.75, 0.25),\n" - " vec2(-0.25, 0.25),\n" - " vec2(-0.25, 0.75)\n" - ");\n" - "\n" - "vec3 colors[11] = vec3[](\n" - " vec3(0.0, 0.0, 1.0),\n" - " vec3(0.0, 1.0, 0.0),\n" - " vec3(0.0, 1.0, 1.0),\n" - " vec3(0.0, 0.0, 1.0),\n" - " vec3(0.0, 1.0, 0.0),\n" - " vec3(0.0, 1.0, 1.0),\n" - " vec3(1.0, 0.0, 0.0),\n" - " vec3(0.0, 0.0, 1.0),\n" - " vec3(0.0, 1.0, 0.0),\n" - " vec3(0.0, 1.0, 1.0),\n" - " vec3(1.0, 0.0, 0.0)\n" - ");\n" - "\n" - "void main() {\n" - " gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0);\n" - " v_fragColor = colors[gl_VertexID];\n" - "}\n"; - -static const char *geometry_shader_source = - "#version 400\n" - "layout(triangles) in;\n" - "layout(triangle_strip, max_vertices = 3) out;\n" - "out vec3 fragColor;\n" - "in vec3 v_fragColor[];\n" - "\n" - "void main() {\n" - " for (int i = 0; i < 3; i++) {\n" - // This should be just: - // gl_Position = gl_in[i].gl_Position; - // fragColor = v_fragColor[0]; - // but we work around an Nvidia Cg compiler bug which seems to - // misdetect above as a passthrough shader and effectively - // replaces the last line with "fragColor = v_fragColor[i];". - // Doing redundant computation seems to fix it. - // TODO: what is the minimal way to avoid the bug? - " gl_Position = gl_in[i].gl_Position + vec4(1.0/16384.0, 1.0/16384.0, 0.0, 0.0);\n" - " precise vec3 color = v_fragColor[0]*(0.999 + gl_in[i].gl_Position.x/16384.0) + v_fragColor[1]*0.00005 + v_fragColor[2]*0.00005;\n" - " fragColor = color;\n" - " EmitVertex();\n" - " }\n" - " EndPrimitive();\n" - "}\n"; - -static const char *fragment_shader_source = - "#version 400\n" - "out vec4 outColor;\n" - "in vec3 fragColor;\n" - "\n" - "void main() {\n" - " outColor = vec4(fragColor, 1.0);\n" - "}\n"; - -static GLuint compile_shader(GLenum type, const char *source) -{ - GLuint shader = glCreateShader(type); - glShaderSource(shader, 1, &source, NULL); - glCompileShader(shader); - - GLint success; - glGetShaderiv(shader, GL_COMPILE_STATUS, &success); - if (!success) { - char log[512]; - glGetShaderInfoLog(shader, sizeof(log), NULL, log); - log[sizeof(log) - 1] = '\0'; - fprintf(stderr, "GL shader type %d compilation failed: %s\n", type, - log); - assert(false); - } - - return shader; -} - -static GLuint create_program(const char *vert_source, const char *geom_source, - const char *frag_source) -{ - GLuint vert_shader = compile_shader(GL_VERTEX_SHADER, vert_source); - GLuint geom_shader = compile_shader(GL_GEOMETRY_SHADER, geom_source); - GLuint frag_shader = compile_shader(GL_FRAGMENT_SHADER, frag_source); - - GLuint shader_prog = glCreateProgram(); - glAttachShader(shader_prog, vert_shader); - glAttachShader(shader_prog, geom_shader); - glAttachShader(shader_prog, frag_shader); - glLinkProgram(shader_prog); - - GLint success; - glGetProgramiv(shader_prog, GL_LINK_STATUS, &success); - if (!success) { - char log[512]; - glGetProgramInfoLog(shader_prog, sizeof(log), NULL, log); - log[sizeof(log) - 1] = '\0'; - fprintf(stderr, "GL shader linking failed: %s\n", log); - assert(false); - } - - glDeleteShader(vert_shader); - glDeleteShader(geom_shader); - glDeleteShader(frag_shader); - - return shader_prog; -} - -static void check_gl_error(const char *context) -{ - GLenum err; - int limit = 10; - - while ((err = glGetError()) != GL_NO_ERROR) { - fprintf(stderr, "GPU properties OpenGL error 0x%X in %s\n", err, - context); - if (--limit <= 0) { - fprintf( - stderr, - "Too many OpenGL errors in %s — possible infinite error loop\n", - context); - break; - } - } -} - -static uint8_t *render_geom_shader_triangles(int width, int height) -{ - // Create the framebuffer and renderbuffer for it - GLuint fbo, rbo; - glGenFramebuffers(1, &fbo); - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - glGenRenderbuffers(1, &rbo); - glBindRenderbuffer(GL_RENDERBUFFER, rbo); - glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height); - check_gl_error("glRenderbufferStorage"); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_RENDERBUFFER, rbo); - check_gl_error("glFramebufferRenderbuffer"); - - assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); - - GLuint shader_prog = create_program( - vertex_shader_source, geometry_shader_source, fragment_shader_source); - assert(shader_prog != 0); - - glUseProgram(shader_prog); - check_gl_error("glUseProgram"); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - check_gl_error("glClear"); - - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glDisable(GL_STENCIL_TEST); - glProvokingVertex(GL_FIRST_VERTEX_CONVENTION); - glViewport(0, 0, width, height); - check_gl_error("state setup"); - - GLuint vao; - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); - check_gl_error("glBindVertexArray"); - glDrawArrays(GL_TRIANGLES, 0, 3); - glDrawArrays(GL_TRIANGLE_STRIP, 3, 4); - glDrawArrays(GL_TRIANGLE_FAN, 7, 4); - check_gl_error("glDrawArrays"); - glFinish(); // glFinish should be unnecessary - - void *pixels = g_malloc(width * height * 4); - assert(pixels != NULL); - glReadBuffer(GL_COLOR_ATTACHMENT0); - glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - check_gl_error("glReadPixels"); - - glBindVertexArray(0); - glDeleteVertexArrays(1, &vao); - glUseProgram(0); - glDeleteProgram(shader_prog); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glDeleteFramebuffers(1, &fbo); - glBindRenderbuffer(GL_RENDERBUFFER, 0); - glDeleteRenderbuffers(1, &rbo); - - return (uint8_t *)pixels; -} - -static bool colors_match(int r1, int g1, int b1, int r2, int g2, int b2) -{ - int dr = r1 - r2; - int dg = g1 - g2; - int db = b1 - b2; - - return (dr * dr + dg * dg + db * db) <= 16; -} - -static int get_color_index(uint8_t *pixel) -{ - int r = pixel[0]; - int g = pixel[1]; - int b = pixel[2]; - - if (colors_match(r, g, b, 0, 0, 255)) { - return 0; - } else if (colors_match(r, g, b, 0, 255, 0)) { - return 1; - } else if (colors_match(r, g, b, 0, 255, 255)) { - return 2; - } else if (colors_match(r, g, b, 255, 0, 0)) { - return 3; - } else { - return -1; - } -} - -static int calc_offset_from_ndc(float x, float y, int width, int height) -{ - int x0 = (int)((x + 1.0f) * width * 0.5f); - int y0 = (int)((y + 1.0f) * height * 0.5f); - - x0 = MAX(x0, 0); - y0 = MAX(y0, 0); - x0 = MIN(x0, width - 1); - y0 = MIN(y0, height - 1); - - return y0 * width + x0; -} - -static void determine_triangle_winding_order(uint8_t *pixels, int width, - int height, GPUProperties *props) -{ - uint8_t *tri_pix = - pixels + calc_offset_from_ndc(-0.5f, -0.5f, width, height) * 4; - uint8_t *strip0_pix = - pixels + calc_offset_from_ndc(0.417f, -0.417f, width, height) * 4; - uint8_t *strip1_pix = - pixels + calc_offset_from_ndc(0.583f, -0.583f, width, height) * 4; - uint8_t *fan_pix = - pixels + calc_offset_from_ndc(-0.583f, 0.417f, width, height) * 4; - uint8_t *fan2_pix = - pixels + calc_offset_from_ndc(-0.417f, 0.583f, width, height) * 4; - - int tri_rot = get_color_index(tri_pix); - if (tri_rot < 0 || tri_rot > 2) { - fprintf(stderr, - "Could not determine triangle rotation, got color: R=%d, G=%d, " - "B=%d\n", - tri_pix[0], tri_pix[1], tri_pix[2]); - tri_rot = 0; - } - props->geom_shader_winding.tri = tri_rot; - - int strip0_rot = get_color_index(strip0_pix); - if (strip0_rot < 0 || strip0_rot > 2) { - fprintf(stderr, - "Could not determine triangle strip0 rotation, got color: " - "R=%d, G=%d, B=%d\n", - strip0_pix[0], strip0_pix[1], strip0_pix[2]); - strip0_rot = 0; - } - int strip1_rot = get_color_index(strip1_pix) - 1; - if (strip1_rot < 0 || strip1_rot > 2) { - fprintf(stderr, - "Could not determine triangle strip1 rotation, got color: " - "R=%d, G=%d, B=%d\n", - strip1_pix[0], strip1_pix[1], strip1_pix[2]); - strip1_rot = 0; - } - props->geom_shader_winding.tri_strip0 = strip0_rot; - props->geom_shader_winding.tri_strip1 = (3 - strip1_rot) % 3; - - int fan_rot = get_color_index(fan_pix); - int fan2_rot = get_color_index(fan2_pix); - if (fan2_rot == 0) { - fan2_rot = 1; - } - fan2_rot--; - if (fan_rot != fan2_rot) { - fprintf(stderr, - "Unexpected inconsistency in triangle fan winding, got colors: " - "R=%d, G=%d, B=%d and R=%d, G=%d, B=%d\n", - fan_pix[0], fan_pix[1], fan_pix[2], fan2_pix[0], fan2_pix[1], - fan2_pix[2]); - fan_rot = 1; - } - if (fan_rot < 0 || fan_rot > 2) { - fprintf(stderr, - "Could not determine triangle fan rotation, got color: R=%d, " - "G=%d, B=%d\n", - fan_pix[0], fan_pix[1], fan_pix[2]); - fan_rot = 1; - } - props->geom_shader_winding.tri_fan = (fan_rot + 2) % 3; -} - -void pgraph_gl_determine_gpu_properties(void) -{ -#ifdef __ANDROID__ - // Skip shader-based probing on Android GLES; some drivers crash here. - pgraph_gl_gpu_properties.geom_shader_winding.tri = 0; - pgraph_gl_gpu_properties.geom_shader_winding.tri_strip0 = 0; - pgraph_gl_gpu_properties.geom_shader_winding.tri_strip1 = 0; - pgraph_gl_gpu_properties.geom_shader_winding.tri_fan = 1; - fprintf(stderr, "GL geometry shader winding: %d, %d, %d, %d (Android default)\n", - pgraph_gl_gpu_properties.geom_shader_winding.tri, - pgraph_gl_gpu_properties.geom_shader_winding.tri_strip0, - pgraph_gl_gpu_properties.geom_shader_winding.tri_strip1, - pgraph_gl_gpu_properties.geom_shader_winding.tri_fan); - return; -#endif - const int width = 640; - const int height = 480; - - uint8_t *pixels = render_geom_shader_triangles(width, height); - determine_triangle_winding_order(pixels, width, height, - &pgraph_gl_gpu_properties); - g_free(pixels); - - fprintf(stderr, "GL geometry shader winding: %d, %d, %d, %d\n", - pgraph_gl_gpu_properties.geom_shader_winding.tri, - pgraph_gl_gpu_properties.geom_shader_winding.tri_strip0, - pgraph_gl_gpu_properties.geom_shader_winding.tri_strip1, - pgraph_gl_gpu_properties.geom_shader_winding.tri_fan); -} - -GPUProperties *pgraph_gl_get_gpu_properties(void) -{ - return &pgraph_gl_gpu_properties; -} diff --git a/hw/xbox/nv2a/pgraph/gl/meson.build b/hw/xbox/nv2a/pgraph/gl/meson.build index c19a542adb..ab25eacb7d 100644 --- a/hw/xbox/nv2a/pgraph/gl/meson.build +++ b/hw/xbox/nv2a/pgraph/gl/meson.build @@ -3,7 +3,6 @@ specific_ss.add([sdl, gloffscreen, files( 'debug.c', 'display.c', 'draw.c', - 'gpuprops.c', 'renderer.c', 'reports.c', 'shaders.c', diff --git a/hw/xbox/nv2a/pgraph/gl/renderer.c b/hw/xbox/nv2a/pgraph/gl/renderer.c index f165d04d51..19bd4f885f 100644 --- a/hw/xbox/nv2a/pgraph/gl/renderer.c +++ b/hw/xbox/nv2a/pgraph/gl/renderer.c @@ -76,13 +76,6 @@ static void early_context_init(void) g_nv2a_context_render = glo_context_create(); g_nv2a_context_display = glo_context_create(); - // Note: Due to use of shared contexts, this must happen after some other - // context is created so the temporary context will not become the thread - // context. After destroying the context, some a durable context should be - // selected. - GloContext *context = glo_context_create(); - pgraph_gl_determine_gpu_properties(); - glo_context_destroy(context); glo_set_current(g_nv2a_context_display); } @@ -283,7 +276,6 @@ static PGRAPHRenderer pgraph_gl_renderer = { .set_surface_scale_factor = pgraph_gl_set_surface_scale_factor, .get_surface_scale_factor = pgraph_gl_get_surface_scale_factor, .get_framebuffer_surface = pgraph_gl_get_framebuffer_surface, - .get_gpu_properties = pgraph_gl_get_gpu_properties, } }; diff --git a/hw/xbox/nv2a/pgraph/gl/renderer.h b/hw/xbox/nv2a/pgraph/gl/renderer.h index 7ac0ab7400..9dd007e8be 100644 --- a/hw/xbox/nv2a/pgraph/gl/renderer.h +++ b/hw/xbox/nv2a/pgraph/gl/renderer.h @@ -300,8 +300,4 @@ void pgraph_gl_shader_write_cache_reload_list(PGRAPHState *pg); void pgraph_gl_set_surface_scale_factor(NV2AState *d, unsigned int scale); unsigned int pgraph_gl_get_surface_scale_factor(NV2AState *d); int pgraph_gl_get_framebuffer_surface(NV2AState *d); -/** Note: The caller must set up a clean GL context before invoking. */ -void pgraph_gl_determine_gpu_properties(void); -GPUProperties *pgraph_gl_get_gpu_properties(void); - #endif diff --git a/hw/xbox/nv2a/pgraph/pgraph.h b/hw/xbox/nv2a/pgraph/pgraph.h index 0449270b55..49f941d450 100644 --- a/hw/xbox/nv2a/pgraph/pgraph.h +++ b/hw/xbox/nv2a/pgraph/pgraph.h @@ -96,15 +96,6 @@ typedef struct BetaState { uint32_t beta; } BetaState; -typedef struct GPUProperties { - struct { - short tri; - short tri_strip0; - short tri_strip1; - short tri_fan; - } geom_shader_winding; -} GPUProperties; - typedef struct PGRAPHRenderer { CONFIG_DISPLAY_RENDERER type; const char *name; @@ -131,7 +122,6 @@ typedef struct PGRAPHRenderer { void (*set_surface_scale_factor)(NV2AState *d, unsigned int scale); unsigned int (*get_surface_scale_factor)(NV2AState *d); int (*get_framebuffer_surface)(NV2AState *d); - GPUProperties *(*get_gpu_properties)(void); } ops; } PGRAPHRenderer; diff --git a/hw/xbox/nv2a/pgraph/vk/gpuprops.c b/hw/xbox/nv2a/pgraph/vk/gpuprops.c deleted file mode 100644 index ecc8bb3852..0000000000 --- a/hw/xbox/nv2a/pgraph/vk/gpuprops.c +++ /dev/null @@ -1,608 +0,0 @@ -/* - * Geforce NV2A PGRAPH Vulkan Renderer - * - * Copyright (c) 2024-2025 Matt Borgerson - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "debug.h" -#include "renderer.h" - -static GPUProperties pgraph_vk_gpu_properties; - -static const char *vertex_shader_source = - "#version 450\n" - "layout(location = 0) out vec3 v_fragColor;\n" - "\n" - "vec2 positions[11] = vec2[](\n" - " vec2(-0.5, -0.75),\n" - " vec2(-0.25, -0.25),\n" - " vec2(-0.75, -0.25),\n" - " vec2(0.25, -0.25),\n" - " vec2(0.25, -0.75),\n" - " vec2(0.75, -0.25),\n" - " vec2(0.75, -0.75),\n" - " vec2(-0.75, 0.75),\n" - " vec2(-0.75, 0.25),\n" - " vec2(-0.25, 0.25),\n" - " vec2(-0.25, 0.75)\n" - ");\n" - "\n" - "vec3 colors[11] = vec3[](\n" - " vec3(0.0, 0.0, 1.0),\n" - " vec3(0.0, 1.0, 0.0),\n" - " vec3(0.0, 1.0, 1.0),\n" - " vec3(0.0, 0.0, 1.0),\n" - " vec3(0.0, 1.0, 0.0),\n" - " vec3(0.0, 1.0, 1.0),\n" - " vec3(1.0, 0.0, 0.0),\n" - " vec3(0.0, 0.0, 1.0),\n" - " vec3(0.0, 1.0, 0.0),\n" - " vec3(0.0, 1.0, 1.0),\n" - " vec3(1.0, 0.0, 0.0)\n" - ");\n" - "\n" - "void main() {\n" - " gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);\n" - " v_fragColor = colors[gl_VertexIndex];\n" - "}\n"; - -static const char *geometry_shader_source = - "#version 450\n" - "layout(triangles) in;\n" - "layout(triangle_strip, max_vertices = 3) out;\n" - "layout(location = 0) out vec3 fragColor;\n" - "layout(location = 0) in vec3 v_fragColor[];\n" - "\n" - "void main() {\n" - " for (int i = 0; i < 3; i++) {\n" - // This should be just: - // gl_Position = gl_in[i].gl_Position; - // fragColor = v_fragColor[0]; - // but we apply the same Nvidia bug work around from gl/gpuprops.c - // to be on the safe side even if the compilers involved with - // Vulkan are different. - " gl_Position = gl_in[i].gl_Position + vec4(1.0/16384.0, 1.0/16384.0, 0.0, 0.0);\n" - " precise vec3 color = v_fragColor[0]*(0.999 + gl_in[i].gl_Position.x/16384.0) + v_fragColor[1]*0.00005 + v_fragColor[2]*0.00005;\n" - " fragColor = color;\n" - " EmitVertex();\n" - " }\n" - " EndPrimitive();\n" - "}\n"; - -static const char *fragment_shader_source = - "#version 450\n" - "layout(location = 0) out vec4 outColor;\n" - "layout(location = 0) in vec3 fragColor;\n" - "\n" - "void main() {\n" - " outColor = vec4(fragColor, 1.0);\n" - "}\n"; - -static VkPipeline create_test_pipeline( - NV2AState *d, VkPrimitiveTopology primitive_topology, - VkShaderModule vert_shader_module, VkShaderModule geom_shader_module, - VkShaderModule frag_shader_module, VkPipelineLayout pipeline_layout, - VkRenderPass render_pass, int width, int height) -{ - PGRAPHState *pg = &d->pgraph; - PGRAPHVkState *r = pg->vk_renderer_state; - - VkPipelineShaderStageCreateInfo shader_stages[] = { - (VkPipelineShaderStageCreateInfo){ - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .stage = VK_SHADER_STAGE_VERTEX_BIT, - .module = vert_shader_module, - .pName = "main", - }, - (VkPipelineShaderStageCreateInfo){ - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .stage = VK_SHADER_STAGE_GEOMETRY_BIT, - .module = geom_shader_module, - .pName = "main", - }, - (VkPipelineShaderStageCreateInfo){ - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .stage = VK_SHADER_STAGE_FRAGMENT_BIT, - .module = frag_shader_module, - .pName = "main", - }, - }; - - VkPipelineVertexInputStateCreateInfo vertex_input_info = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, - .vertexBindingDescriptionCount = 0, - .vertexAttributeDescriptionCount = 0, - }; - - VkPipelineInputAssemblyStateCreateInfo input_assembly = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, - .topology = primitive_topology, - .primitiveRestartEnable = VK_FALSE, - }; - - VkViewport viewport = { - .x = 0.0f, - .y = 0.0f, - .width = (float)width, - .height = (float)height, - .minDepth = 0.0f, - .maxDepth = 1.0f, - }; - VkRect2D scissor = { - .offset = { 0, 0 }, - .extent.width = width, - .extent.height = height, - }; - VkPipelineViewportStateCreateInfo viewport_state = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, - .viewportCount = 1, - .pViewports = &viewport, - .scissorCount = 1, - .pScissors = &scissor, - }; - - VkPipelineRasterizationStateCreateInfo rasterizer = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, - .depthClampEnable = VK_FALSE, - .rasterizerDiscardEnable = VK_FALSE, - .polygonMode = VK_POLYGON_MODE_FILL, - .lineWidth = 1.0f, - .cullMode = VK_CULL_MODE_BACK_BIT, - .frontFace = VK_FRONT_FACE_CLOCKWISE, - .depthBiasEnable = VK_FALSE, - }; - - VkPipelineMultisampleStateCreateInfo multisampling = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, - .sampleShadingEnable = VK_FALSE, - .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, - }; - - VkPipelineColorBlendAttachmentState color_blend_attachment = { - .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | - VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, - .blendEnable = VK_FALSE, - }; - - VkPipelineColorBlendStateCreateInfo color_blending = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, - .logicOpEnable = VK_FALSE, - .logicOp = VK_LOGIC_OP_COPY, - .attachmentCount = 1, - .pAttachments = &color_blend_attachment, - .blendConstants = { 0.0f, 0.0f, 0.0f, 0.0f }, - }; - - VkGraphicsPipelineCreateInfo pipeline_info = { - .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .stageCount = ARRAY_SIZE(shader_stages), - .pStages = shader_stages, - .pVertexInputState = &vertex_input_info, - .pInputAssemblyState = &input_assembly, - .pViewportState = &viewport_state, - .pRasterizationState = &rasterizer, - .pMultisampleState = &multisampling, - .pColorBlendState = &color_blending, - .layout = pipeline_layout, - .renderPass = render_pass, - .subpass = 0, - .basePipelineHandle = VK_NULL_HANDLE, - }; - - VkPipeline pipeline; - VK_CHECK(vkCreateGraphicsPipelines(r->device, VK_NULL_HANDLE, 1, - &pipeline_info, NULL, &pipeline)); - - return pipeline; -} - -static uint8_t *render_geom_shader_triangles(NV2AState *d, int width, - int height) -{ - PGRAPHState *pg = &d->pgraph; - PGRAPHVkState *r = pg->vk_renderer_state; - - // Create image - VkImageCreateInfo image_create_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .imageType = VK_IMAGE_TYPE_2D, - .extent.width = width, - .extent.height = height, - .extent.depth = 1, - .mipLevels = 1, - .arrayLayers = 1, - .format = VK_FORMAT_R8G8B8A8_UNORM, - .tiling = VK_IMAGE_TILING_OPTIMAL, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | - VK_IMAGE_USAGE_TRANSFER_SRC_BIT, - .samples = VK_SAMPLE_COUNT_1_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - }; - - VkImage offscreen_image; - VK_CHECK( - vkCreateImage(r->device, &image_create_info, NULL, &offscreen_image)); - - // Allocate and bind image memory - VkMemoryRequirements memory_requirements; - vkGetImageMemoryRequirements(r->device, offscreen_image, - &memory_requirements); - - VkMemoryAllocateInfo alloc_info = { - .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - .allocationSize = memory_requirements.size, - .memoryTypeIndex = - pgraph_vk_get_memory_type(pg, memory_requirements.memoryTypeBits, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT), - }; - - VkDeviceMemory image_memory; - VK_CHECK(vkAllocateMemory(r->device, &alloc_info, NULL, &image_memory)); - VK_CHECK(vkBindImageMemory(r->device, offscreen_image, image_memory, 0)); - - // Create Image View - VkImageViewCreateInfo image_view_create_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = offscreen_image, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = image_create_info.format, - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }; - - VkImageView offscreen_image_view; - VK_CHECK(vkCreateImageView(r->device, &image_view_create_info, NULL, - &offscreen_image_view)); - - // Buffer for image CPU access - VkBufferCreateInfo buffer_info = { - .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .size = width * height * 4, // RGBA8 = 4 bytes per pixel - .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - }; - - VkBuffer cpu_buffer; - VK_CHECK(vkCreateBuffer(r->device, &buffer_info, NULL, &cpu_buffer)); - - // Allocate and bind memory for image CPU access - VkMemoryRequirements host_mem_requirements; - vkGetBufferMemoryRequirements(r->device, cpu_buffer, - &host_mem_requirements); - - VkMemoryAllocateInfo host_alloc_info = { - .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - .allocationSize = host_mem_requirements.size, - .memoryTypeIndex = - pgraph_vk_get_memory_type(pg, host_mem_requirements.memoryTypeBits, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT), - }; - - VkDeviceMemory cpu_buffer_memory; - VK_CHECK(vkAllocateMemory(r->device, &host_alloc_info, NULL, - &cpu_buffer_memory)); - VK_CHECK(vkBindBufferMemory(r->device, cpu_buffer, cpu_buffer_memory, 0)); - - - VkAttachmentDescription color_attachment = { - .format = VK_FORMAT_R8G8B8A8_UNORM, - .samples = VK_SAMPLE_COUNT_1_BIT, - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - }; - - VkAttachmentReference color_ref = { - 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL - }; - - VkSubpassDescription subpass = { - .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, - .colorAttachmentCount = 1, - .pColorAttachments = &color_ref, - }; - - VkRenderPassCreateInfo render_pass_info = { - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, - .attachmentCount = 1, - .pAttachments = &color_attachment, - .subpassCount = 1, - .pSubpasses = &subpass, - }; - - VkRenderPass render_pass; - VK_CHECK( - vkCreateRenderPass(r->device, &render_pass_info, NULL, &render_pass)); - - VkFramebufferCreateInfo fb_info = { - .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - .renderPass = render_pass, - .attachmentCount = 1, - .pAttachments = &offscreen_image_view, - .width = width, - .height = height, - .layers = 1, - }; - - VkFramebuffer framebuffer; - VK_CHECK(vkCreateFramebuffer(r->device, &fb_info, NULL, &framebuffer)); - - ShaderModuleInfo *vsh_info = pgraph_vk_create_shader_module_from_glsl( - r, VK_SHADER_STAGE_VERTEX_BIT, vertex_shader_source); - ShaderModuleInfo *geom_info = pgraph_vk_create_shader_module_from_glsl( - r, VK_SHADER_STAGE_GEOMETRY_BIT, geometry_shader_source); - ShaderModuleInfo *psh_info = pgraph_vk_create_shader_module_from_glsl( - r, VK_SHADER_STAGE_FRAGMENT_BIT, fragment_shader_source); - - VkShaderModule vert_shader_module = vsh_info->module; - VkShaderModule geom_shader_module = geom_info->module; - VkShaderModule frag_shader_module = psh_info->module; - - VkPipelineLayoutCreateInfo pipeline_layout_info = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .setLayoutCount = 0, - .pushConstantRangeCount = 0, - }; - - VkPipelineLayout pipeline_layout; - VK_CHECK(vkCreatePipelineLayout(r->device, &pipeline_layout_info, NULL, - &pipeline_layout)); - - VkPipeline tri_pipeline = create_test_pipeline( - d, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, vert_shader_module, - geom_shader_module, frag_shader_module, pipeline_layout, render_pass, - width, height); - - VkPipeline strip_pipeline = create_test_pipeline( - d, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, vert_shader_module, - geom_shader_module, frag_shader_module, pipeline_layout, render_pass, - width, height); - - VkPipeline fan_pipeline = create_test_pipeline( - d, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, vert_shader_module, - geom_shader_module, frag_shader_module, pipeline_layout, render_pass, - width, height); - - pgraph_vk_destroy_shader_module(r, psh_info); - pgraph_vk_destroy_shader_module(r, geom_info); - pgraph_vk_destroy_shader_module(r, vsh_info); - - VkCommandBufferBeginInfo begin_info = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - }; - VK_CHECK(vkBeginCommandBuffer(r->command_buffer, &begin_info)); - - // Begin render pass - VkClearValue clear_color = { - .color.float32 = { 0.0f, 0.0f, 0.0f, 1.0f }, - }; - VkRenderPassBeginInfo rp_begin = { - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - .renderPass = render_pass, - .framebuffer = framebuffer, - .renderArea.extent.width = width, - .renderArea.extent.height = height, - .clearValueCount = 1, - .pClearValues = &clear_color, - }; - - vkCmdBeginRenderPass(r->command_buffer, &rp_begin, - VK_SUBPASS_CONTENTS_INLINE); - - vkCmdBindPipeline(r->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - tri_pipeline); - vkCmdDraw(r->command_buffer, 3, 1, 0, 0); - vkCmdBindPipeline(r->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - strip_pipeline); - vkCmdDraw(r->command_buffer, 4, 1, 3, 0); - vkCmdBindPipeline(r->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - fan_pipeline); - vkCmdDraw(r->command_buffer, 4, 1, 7, 0); - - vkCmdEndRenderPass(r->command_buffer); - - // Synchronize and transition framebuffer for copying to CPU - pgraph_vk_transition_image_layout(pg, r->command_buffer, offscreen_image, - image_create_info.format, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - - // Copy framebuffer to CPU memory - VkBufferImageCopy region = { - .bufferOffset = 0, - .bufferRowLength = 0, // tightly packed - .bufferImageHeight = 0, - - .imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .imageSubresource.mipLevel = 0, - .imageSubresource.baseArrayLayer = 0, - .imageSubresource.layerCount = 1, - - .imageOffset = { 0, 0, 0 }, - .imageExtent = { width, height, 1 }, - }; - - vkCmdCopyImageToBuffer(r->command_buffer, offscreen_image, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, cpu_buffer, 1, - ®ion); - - VK_CHECK(vkEndCommandBuffer(r->command_buffer)); - - VkSubmitInfo submit_info = { - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, - .commandBufferCount = 1, - .pCommandBuffers = &r->command_buffer, - }; - - VK_CHECK(vkQueueSubmit(r->queue, 1, &submit_info, VK_NULL_HANDLE)); - VK_CHECK(vkQueueWaitIdle(r->queue)); - - void *data; - VK_CHECK( - vkMapMemory(r->device, cpu_buffer_memory, 0, VK_WHOLE_SIZE, 0, &data)); - void *pixels = g_malloc(width * height * 4); - assert(pixels != NULL); - memcpy(pixels, data, width * height * 4); - vkUnmapMemory(r->device, cpu_buffer_memory); - - vkDestroyPipeline(r->device, strip_pipeline, NULL); - vkDestroyPipeline(r->device, fan_pipeline, NULL); - vkDestroyPipeline(r->device, tri_pipeline, NULL); - vkDestroyPipelineLayout(r->device, pipeline_layout, NULL); - vkDestroyFramebuffer(r->device, framebuffer, NULL); - vkDestroyRenderPass(r->device, render_pass, NULL); - vkDestroyImageView(r->device, offscreen_image_view, NULL); - vkDestroyBuffer(r->device, cpu_buffer, NULL); - vkFreeMemory(r->device, cpu_buffer_memory, NULL); - vkDestroyImage(r->device, offscreen_image, NULL); - vkFreeMemory(r->device, image_memory, NULL); - - return (uint8_t *)pixels; -} - -static bool colors_match(int r1, int g1, int b1, int r2, int g2, int b2) -{ - int dr = r1 - r2; - int dg = g1 - g2; - int db = b1 - b2; - - return (dr * dr + dg * dg + db * db) <= 16; -} - -static int get_color_index(uint8_t *pixel) -{ - int r = pixel[0]; - int g = pixel[1]; - int b = pixel[2]; - - if (colors_match(r, g, b, 0, 0, 255)) { - return 0; - } else if (colors_match(r, g, b, 0, 255, 0)) { - return 1; - } else if (colors_match(r, g, b, 0, 255, 255)) { - return 2; - } else if (colors_match(r, g, b, 255, 0, 0)) { - return 3; - } else { - return -1; - } -} - -static int calc_offset_from_ndc(float x, float y, int width, int height) -{ - int x0 = (int)((x + 1.0f) * width * 0.5f); - int y0 = (int)((y + 1.0f) * height * 0.5f); - - x0 = MAX(x0, 0); - y0 = MAX(y0, 0); - x0 = MIN(x0, width - 1); - y0 = MIN(y0, height - 1); - - return y0 * width + x0; -} - -static void determine_triangle_winding_order(uint8_t *pixels, int width, - int height, GPUProperties *props) -{ - uint8_t *tri_pix = - pixels + calc_offset_from_ndc(-0.5f, -0.5f, width, height) * 4; - uint8_t *strip0_pix = - pixels + calc_offset_from_ndc(0.417f, -0.417f, width, height) * 4; - uint8_t *strip1_pix = - pixels + calc_offset_from_ndc(0.583f, -0.583f, width, height) * 4; - uint8_t *fan_pix = - pixels + calc_offset_from_ndc(-0.583f, 0.417f, width, height) * 4; - uint8_t *fan2_pix = - pixels + calc_offset_from_ndc(-0.417f, 0.583f, width, height) * 4; - - int tri_rot = get_color_index(tri_pix); - if (tri_rot < 0 || tri_rot > 2) { - fprintf(stderr, - "Could not determine triangle rotation, got color: R=%d, G=%d, " - "B=%d\n", - tri_pix[0], tri_pix[1], tri_pix[2]); - tri_rot = 0; - } - props->geom_shader_winding.tri = tri_rot; - - int strip0_rot = get_color_index(strip0_pix); - if (strip0_rot < 0 || strip0_rot > 2) { - fprintf(stderr, - "Could not determine triangle strip0 rotation, got color: " - "R=%d, G=%d, B=%d\n", - strip0_pix[0], strip0_pix[1], strip0_pix[2]); - strip0_rot = 0; - } - int strip1_rot = get_color_index(strip1_pix) - 1; - if (strip1_rot < 0 || strip1_rot > 2) { - fprintf(stderr, - "Could not determine triangle strip1 rotation, got color: " - "R=%d, G=%d, B=%d\n", - strip1_pix[0], strip1_pix[1], strip1_pix[2]); - strip1_rot = 0; - } - props->geom_shader_winding.tri_strip0 = strip0_rot; - props->geom_shader_winding.tri_strip1 = (3 - strip1_rot) % 3; - - int fan_rot = get_color_index(fan_pix); - int fan2_rot = get_color_index(fan2_pix); - if (fan2_rot == 0) { - fan2_rot = 1; - } - fan2_rot--; - if (fan_rot != fan2_rot) { - fprintf(stderr, - "Unexpected inconsistency in triangle fan winding, got colors: " - "R=%d, G=%d, B=%d and R=%d, G=%d, B=%d\n", - fan_pix[0], fan_pix[1], fan_pix[2], fan2_pix[0], fan2_pix[1], - fan2_pix[2]); - fan_rot = 1; - } - if (fan_rot < 0 || fan_rot > 2) { - fprintf(stderr, - "Could not determine triangle fan rotation, got color: R=%d, " - "G=%d, B=%d\n", - fan_pix[0], fan_pix[1], fan_pix[2]); - fan_rot = 1; - } - props->geom_shader_winding.tri_fan = (fan_rot + 2) % 3; -} - -void pgraph_vk_determine_gpu_properties(NV2AState *d) -{ - const int width = 640; - const int height = 480; - - uint8_t *pixels = render_geom_shader_triangles(d, width, height); - determine_triangle_winding_order(pixels, width, height, - &pgraph_vk_gpu_properties); - g_free(pixels); - - fprintf(stderr, "VK geometry shader winding: %d, %d, %d, %d\n", - pgraph_vk_gpu_properties.geom_shader_winding.tri, - pgraph_vk_gpu_properties.geom_shader_winding.tri_strip0, - pgraph_vk_gpu_properties.geom_shader_winding.tri_strip1, - pgraph_vk_gpu_properties.geom_shader_winding.tri_fan); -} - -GPUProperties *pgraph_vk_get_gpu_properties(void) -{ - return &pgraph_vk_gpu_properties; -} diff --git a/hw/xbox/nv2a/pgraph/vk/meson.build b/hw/xbox/nv2a/pgraph/vk/meson.build index b5d75e2faa..24c2474cb9 100644 --- a/hw/xbox/nv2a/pgraph/vk/meson.build +++ b/hw/xbox/nv2a/pgraph/vk/meson.build @@ -9,7 +9,6 @@ specific_ss.add([sdl, volk, libglslang, vma, vulkan, spirv_reflect, gloffscreen, 'display.c', 'draw.c', 'glsl.c', - 'gpuprops.c', 'image.c', 'instance.c', 'renderer.c', diff --git a/hw/xbox/nv2a/pgraph/vk/renderer.c b/hw/xbox/nv2a/pgraph/vk/renderer.c index 90d3536f45..c3184ba434 100644 --- a/hw/xbox/nv2a/pgraph/vk/renderer.c +++ b/hw/xbox/nv2a/pgraph/vk/renderer.c @@ -96,7 +96,6 @@ static void pgraph_vk_init(NV2AState *d, Error **errp) pgraph_vk_update_vertex_ram_buffer(&d->pgraph, 0, d->vram_ptr, memory_region_size(d->vram)); - pgraph_vk_determine_gpu_properties(d); } static void pgraph_vk_finalize(NV2AState *d) @@ -269,7 +268,6 @@ static PGRAPHRenderer pgraph_vk_renderer = { .set_surface_scale_factor = pgraph_vk_set_surface_scale_factor, .get_surface_scale_factor = pgraph_vk_get_surface_scale_factor, .get_framebuffer_surface = pgraph_vk_get_framebuffer_surface, - .get_gpu_properties = pgraph_vk_get_gpu_properties, } }; diff --git a/hw/xbox/nv2a/pgraph/vk/renderer.h b/hw/xbox/nv2a/pgraph/vk/renderer.h index cc93fe6231..711a3b85eb 100644 --- a/hw/xbox/nv2a/pgraph/vk/renderer.h +++ b/hw/xbox/nv2a/pgraph/vk/renderer.h @@ -597,8 +597,4 @@ void pgraph_vk_end_nondraw_commands(PGRAPHState *pg, VkCommandBuffer cmd); // blit.c void pgraph_vk_image_blit(NV2AState *d); -// gpuprops.c -void pgraph_vk_determine_gpu_properties(NV2AState *d); -GPUProperties *pgraph_vk_get_gpu_properties(void); - #endif