diff --git a/Makefile b/Makefile index 7b610e7..00d853d 100644 --- a/Makefile +++ b/Makefile @@ -163,12 +163,12 @@ ifeq ($(COMPILER),ido) else ifeq ($(COMPILER),gcc) NON_MATCHING := 1 MIPSISET := -mips3 - OPT_FLAGS := -O2 + OPT_FLAGS := - endif # OPT_FLAGS - for ports ifeq ($(TARGET_N64),0) - OPT_FLAGS := -O2 -g + OPT_FLAGS := -g ifeq ($(TARGET_WEB),1) OPT_FLAGS += -g4 --source-map-base http://localhost:8080/ endif @@ -499,7 +499,7 @@ ifeq ($(ENABLE_OPENGL),1) endif ifeq ($(TARGET_LINUX),1) GFX_CFLAGS += $(shell sdl2-config --cflags) - GFX_LDFLAGS += -lGL $(shell sdl2-config --libs) -lX11 -lXrandr -lvulkan + GFX_LDFLAGS += -lGL $(shell sdl2-config --libs) -lX11 -lXrandr -lvulkan -lglslang -lSPIRV endif ifeq ($(TARGET_WEB),1) GFX_CFLAGS += -s USE_SDL=2 diff --git a/src/pc/gfx/defaultresources.hpp b/src/pc/gfx/defaultresources.hpp new file mode 100755 index 0000000..2c12b89 --- /dev/null +++ b/src/pc/gfx/defaultresources.hpp @@ -0,0 +1,109 @@ +#pragma once + +const TBuiltInResource DefaultTBuiltInResource = { + /* .MaxLights = */ 32, + /* .MaxClipPlanes = */ 6, + /* .MaxTextureUnits = */ 32, + /* .MaxTextureCoords = */ 32, + /* .MaxVertexAttribs = */ 64, + /* .MaxVertexUniformComponents = */ 4096, + /* .MaxVaryingFloats = */ 64, + /* .MaxVertexTextureImageUnits = */ 32, + /* .MaxCombinedTextureImageUnits = */ 80, + /* .MaxTextureImageUnits = */ 32, + /* .MaxFragmentUniformComponents = */ 4096, + /* .MaxDrawBuffers = */ 32, + /* .MaxVertexUniformVectors = */ 128, + /* .MaxVaryingVectors = */ 8, + /* .MaxFragmentUniformVectors = */ 16, + /* .MaxVertexOutputVectors = */ 16, + /* .MaxFragmentInputVectors = */ 15, + /* .MinProgramTexelOffset = */ -8, + /* .MaxProgramTexelOffset = */ 7, + /* .MaxClipDistances = */ 8, + /* .MaxComputeWorkGroupCountX = */ 65535, + /* .MaxComputeWorkGroupCountY = */ 65535, + /* .MaxComputeWorkGroupCountZ = */ 65535, + /* .MaxComputeWorkGroupSizeX = */ 1024, + /* .MaxComputeWorkGroupSizeY = */ 1024, + /* .MaxComputeWorkGroupSizeZ = */ 64, + /* .MaxComputeUniformComponents = */ 1024, + /* .MaxComputeTextureImageUnits = */ 16, + /* .MaxComputeImageUniforms = */ 8, + /* .MaxComputeAtomicCounters = */ 8, + /* .MaxComputeAtomicCounterBuffers = */ 1, + /* .MaxVaryingComponents = */ 60, + /* .MaxVertexOutputComponents = */ 64, + /* .MaxGeometryInputComponents = */ 64, + /* .MaxGeometryOutputComponents = */ 128, + /* .MaxFragmentInputComponents = */ 128, + /* .MaxImageUnits = */ 8, + /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 8, + /* .MaxCombinedShaderOutputResources = */ 8, + /* .MaxImageSamples = */ 0, + /* .MaxVertexImageUniforms = */ 0, + /* .MaxTessControlImageUniforms = */ 0, + /* .MaxTessEvaluationImageUniforms = */ 0, + /* .MaxGeometryImageUniforms = */ 0, + /* .MaxFragmentImageUniforms = */ 8, + /* .MaxCombinedImageUniforms = */ 8, + /* .MaxGeometryTextureImageUnits = */ 16, + /* .MaxGeometryOutputVertices = */ 256, + /* .MaxGeometryTotalOutputComponents = */ 1024, + /* .MaxGeometryUniformComponents = */ 1024, + /* .MaxGeometryVaryingComponents = */ 64, + /* .MaxTessControlInputComponents = */ 128, + /* .MaxTessControlOutputComponents = */ 128, + /* .MaxTessControlTextureImageUnits = */ 16, + /* .MaxTessControlUniformComponents = */ 1024, + /* .MaxTessControlTotalOutputComponents = */ 4096, + /* .MaxTessEvaluationInputComponents = */ 128, + /* .MaxTessEvaluationOutputComponents = */ 128, + /* .MaxTessEvaluationTextureImageUnits = */ 16, + /* .MaxTessEvaluationUniformComponents = */ 1024, + /* .MaxTessPatchComponents = */ 120, + /* .MaxPatchVertices = */ 32, + /* .MaxTessGenLevel = */ 64, + /* .MaxViewports = */ 16, + /* .MaxVertexAtomicCounters = */ 0, + /* .MaxTessControlAtomicCounters = */ 0, + /* .MaxTessEvaluationAtomicCounters = */ 0, + /* .MaxGeometryAtomicCounters = */ 0, + /* .MaxFragmentAtomicCounters = */ 8, + /* .MaxCombinedAtomicCounters = */ 8, + /* .MaxAtomicCounterBindings = */ 1, + /* .MaxVertexAtomicCounterBuffers = */ 0, + /* .MaxTessControlAtomicCounterBuffers = */ 0, + /* .MaxTessEvaluationAtomicCounterBuffers = */ 0, + /* .MaxGeometryAtomicCounterBuffers = */ 0, + /* .MaxFragmentAtomicCounterBuffers = */ 1, + /* .MaxCombinedAtomicCounterBuffers = */ 1, + /* .MaxAtomicCounterBufferSize = */ 16384, + /* .MaxTransformFeedbackBuffers = */ 4, + /* .MaxTransformFeedbackInterleavedComponents = */ 64, + /* .MaxCullDistances = */ 8, + /* .MaxCombinedClipAndCullDistances = */ 8, + /* .MaxSamples = */ 4, + /* .maxMeshOutputVerticesNV = */ 256, + /* .maxMeshOutputPrimitivesNV = */ 512, + /* .maxMeshWorkGroupSizeX_NV = */ 32, + /* .maxMeshWorkGroupSizeY_NV = */ 1, + /* .maxMeshWorkGroupSizeZ_NV = */ 1, + /* .maxTaskWorkGroupSizeX_NV = */ 32, + /* .maxTaskWorkGroupSizeY_NV = */ 1, + /* .maxTaskWorkGroupSizeZ_NV = */ 1, + /* .maxMeshViewCountNV = */ 4, + /* .maxDualSourceDrawBuffersEXT = */ 1, + + /* .limits = */ + { + /* .nonInductiveForLoops = */ 1, + /* .whileLoops = */ 1, + /* .doWhileLoops = */ 1, + /* .generalUniformIndexing = */ 1, + /* .generalAttributeMatrixVectorIndexing = */ 1, + /* .generalVaryingIndexing = */ 1, + /* .generalSamplerIndexing = */ 1, + /* .generalVariableIndexing = */ 1, + /* .generalConstantMatrixVectorIndexing = */ 1, + }}; diff --git a/src/pc/gfx/gfx_vulkan.cpp b/src/pc/gfx/gfx_vulkan.cpp index 9d0995a..2cb09d7 100644 --- a/src/pc/gfx/gfx_vulkan.cpp +++ b/src/pc/gfx/gfx_vulkan.cpp @@ -7,11 +7,14 @@ #include #include #include +#include +#include #include "gfx_rendering_api.h" #include "gfx_pc.h" #include "gfx_window_manager_api.h" #include "gfx_cc.h" +#include "defaultresources.hpp" static VkInstance instance = VK_NULL_HANDLE; static VkPhysicalDevice physical_device = VK_NULL_HANDLE; @@ -36,14 +39,21 @@ static std::array render_finished; static std::array in_flight; static uint32_t current_frame = 0; static uint32_t image_index = 0; -static VkCommandBuffer current_cmd = VK_NULL_HANDLE; -static VkPipeline pipeline = VK_NULL_HANDLE; -static VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; +static VkCommandBuffer current_cmd = VK_NULL_HANDLE; static VkBuffer vertex_buffer = VK_NULL_HANDLE; static VkDeviceMemory vertex_memory = VK_NULL_HANDLE; +struct ShaderProgramVulkan { + VkPipeline pipeline = VK_NULL_HANDLE; + VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; +}; + +std::unordered_map cached_shaders; + +static ShaderProgramVulkan* last_program = nullptr; + VKAPI_ATTR VkBool32 VKAPI_CALL gfx_vulkan_debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, @@ -84,29 +94,209 @@ static VkShaderModule gfx_vulkan_load_shader(const char* path) { return gfx_vulkan_create_shader_module(reinterpret_cast(buffer.data()), fileSize); } -// TODO: currently no-op for testing triangle draw -static void gfx_vulkan_create_pipeline() { +static std::string gfx_vulkan_create_vertex_shader(const CCFeatures features) { + std::string src = "#version 460 core\n"; + + const bool uses_textures = features.used_textures[0] || features.used_textures[1]; + const bool uses_alpha = features.opt_alpha; + const bool uses_fog = features.opt_fog; + const bool needs_screenpos = features.opt_alpha && features.opt_noise; + + src += "layout(location = 0) in vec4 position;\n"; + + int next_input = 1; + if(uses_textures) { + src += "layout(location = " + std::to_string(next_input++) + ") in vec2 uv;\n"; + } + + if(needs_screenpos) { + src += "layout(location = " + std::to_string(next_input++) + ") in vec4 screen_pos;\n"; + } + + if(uses_fog) { + src += "layout(location = " + std::to_string(next_input++) + ") in vec4 fog;\n"; + } + + // extra inputs? + for(int i = 0; i < features.num_inputs; i++) { + int size = uses_alpha ? 4 : 3; + src += "layout(location = " + std::to_string(next_input++) + ") in vec" + std::to_string(size) + " input" + std::to_string(i + 1) + ";\n"; + } + + src += "void main() {\n"; + + src += "gl_Position = position;\n"; + + src += "}"; + + return src; +} + +static std::string gfx_vulkan_create_fragment_shader(const CCFeatures features) { + std::string src = "#version 460 core\n"; + + const bool uses_textures = features.used_textures[0] || features.used_textures[1]; + const bool uses_alpha = features.opt_alpha; + const bool uses_fog = features.opt_fog; + const bool needs_screenpos = features.opt_alpha && features.opt_noise; + + src += "layout(location = 0) out vec4 out_color;\n"; + + /*int next_input = 0; + if(uses_textures) { + src += "layout(location = " + std::to_string(next_input++) + ") in vec2 uv;\n"; + } + + if(needs_screenpos) { + src += "layout(location = " + std::to_string(next_input++) + ") in vec4 screen_pos;\n"; + } + + if(uses_fog) { + src += "layout(location = " + std::to_string(next_input++) + ") in vec4 fog;\n"; + } + + // extra inputs? + for(int i = 0; i < features.num_inputs; i++) { + int size = uses_alpha ? 4 : 3; + src += "layout(location = " + std::to_string(next_input++) + ") in vec" + std::to_string(size) + " input" + std::to_string(i + 1) + ";\n"; + }*/ + + src += "void main() {\n"; + + src += "out_color = vec4(1, 1, 0, 1);\n"; + + src += "}"; + + return src; +} + +static VkShaderModule gfx_vulkan_compile_glsl(const std::string& shader_src, const EShLanguage language) { + glslang::InitializeProcess(); // TODO: lol + + const char* InputCString = shader_src.c_str(); + + glslang::TShader shader(language); + shader.setStrings(&InputCString, 1); + + int ClientInputSemanticsVersion = 100; // maps to, say, #define VULKAN 100 + + shader.setEnvInput(glslang::EShSourceGlsl, language, glslang::EShClientVulkan, ClientInputSemanticsVersion); + + // we are targeting vulkan 1.1, so that uses SPIR-V 1.3 + shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0); + shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_0); + + if (!shader.parse(&DefaultTBuiltInResource, 100, false, EShMsgDefault)) { + printf("%s\n", shader.getInfoLog()); + + return {}; + } + + glslang::TProgram Program; + Program.addShader(&shader); + + if (!Program.link(EShMsgDefault)) { + printf("failed to link %s %s %s\n", shader_src.data(), shader.getInfoLog(), shader.getInfoDebugLog()); + + return VK_NULL_HANDLE; + } + + std::vector SpirV; + spv::SpvBuildLogger logger; + + glslang::SpvOptions spvOptions; + spvOptions.generateDebugInfo = true; + + glslang::GlslangToSpv(*Program.getIntermediate(language), SpirV, &logger, &spvOptions); + + return gfx_vulkan_create_shader_module(SpirV.data(), SpirV.size() * sizeof(uint32_t)); +} + +static std::pair gfx_vulkan_create_pipeline(const CCFeatures features) { + const std::string vertex_src = gfx_vulkan_create_vertex_shader(features); + const std::string fragment_src = gfx_vulkan_create_fragment_shader(features); + VkPipelineShaderStageCreateInfo vertex_stage = {}; vertex_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; vertex_stage.stage = VK_SHADER_STAGE_VERTEX_BIT; - vertex_stage.module = gfx_vulkan_load_shader("vert.spv"); + vertex_stage.module = gfx_vulkan_compile_glsl(vertex_src, EShLanguage::EShLangVertex); vertex_stage.pName = "main"; VkPipelineShaderStageCreateInfo fragment_stage = {}; fragment_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; fragment_stage.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - fragment_stage.module = gfx_vulkan_load_shader("frag.spv"); + fragment_stage.module = gfx_vulkan_compile_glsl(fragment_src, EShLanguage::EShLangFragment); fragment_stage.pName = "main"; std::array shader_stages = { vertex_stage, fragment_stage }; - VkVertexInputBindingDescription binding = {}; - binding.stride = sizeof(float) * 4; + const bool uses_textures = features.used_textures[0] || features.used_textures[1]; + const bool uses_alpha = features.opt_alpha; + const bool uses_fog = features.opt_fog; + const bool needs_screenpos = features.opt_alpha && features.opt_noise; + + int num_floats = 0; + int current_location = 1; + + std::vector attributes; VkVertexInputAttributeDescription position_attribute = {}; position_attribute.format = VK_FORMAT_R32G32B32A32_SFLOAT; + num_floats += 4; - const std::array attributes = { position_attribute }; + attributes.push_back(position_attribute); + + if(uses_textures) { + VkVertexInputAttributeDescription uv_attribute = {}; + uv_attribute.format = VK_FORMAT_R32G32_SFLOAT; + uv_attribute.location = current_location++; + uv_attribute.offset = num_floats * sizeof(float); + + num_floats += 2; + + attributes.push_back(uv_attribute); + } + + if(needs_screenpos) { + VkVertexInputAttributeDescription screenpos_attribute = {}; + screenpos_attribute.format = VK_FORMAT_R32G32B32A32_SFLOAT; + screenpos_attribute.location = current_location++; + screenpos_attribute.offset = num_floats * sizeof(float); + + num_floats += 4; + + attributes.push_back(screenpos_attribute); + } + + if(uses_fog) { + VkVertexInputAttributeDescription fog_attribute = {}; + fog_attribute.format = VK_FORMAT_R32G32B32A32_SFLOAT; + fog_attribute.location = current_location++; + fog_attribute.offset = num_floats * sizeof(float); + + num_floats += 4; + + attributes.push_back(fog_attribute); + } + + for(int i = 0; i < features.num_inputs; i++) { + VkVertexInputAttributeDescription extra_attribute = {}; + extra_attribute.format = VK_FORMAT_R32G32B32A32_SFLOAT; + extra_attribute.location = current_location++; + extra_attribute.offset = num_floats * sizeof(float); + + num_floats += 4; + + attributes.push_back(extra_attribute); + } + + printf("uses textures: %d\n", uses_textures); + printf("needs screenpos: %d\n", needs_screenpos); + printf("uses fog: %d\n", uses_fog); + printf("extra inputs: %d\n", features.num_inputs); + + VkVertexInputBindingDescription binding = {}; + binding.stride = sizeof(float) * num_floats; VkPipelineVertexInputStateCreateInfo vertex_input_state = {}; vertex_input_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; @@ -159,7 +349,9 @@ static void gfx_vulkan_create_pipeline() { VkPipelineLayoutCreateInfo pipeline_layout_create_info = {}; pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - vkCreatePipelineLayout(device, &pipeline_layout_create_info, nullptr, &pipeline_layout); + VkPipelineLayout layout = VK_NULL_HANDLE; + + vkCreatePipelineLayout(device, &pipeline_layout_create_info, nullptr, &layout); VkPipelineDepthStencilStateCreateInfo depth_stencil = {}; depth_stencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; @@ -180,20 +372,38 @@ static void gfx_vulkan_create_pipeline() { pipeline_create_info.pColorBlendState = &color_blending; pipeline_create_info.pDynamicState = &dynamic_state; pipeline_create_info.pDepthStencilState = &depth_stencil; - pipeline_create_info.layout = pipeline_layout; + pipeline_create_info.layout = layout; pipeline_create_info.renderPass = render_pass; + VkPipeline pipeline = VK_NULL_HANDLE; vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipeline_create_info, nullptr, &pipeline); + + return {pipeline, layout}; } +// FIXME: reorganize +static void gfx_vulkan_renderer_load_shader(struct ShaderProgram *new_prg); + static struct ShaderProgram *gfx_vulkan_renderer_create_and_load_new_shader(uint32_t shader_id) { - gfx_vulkan_create_pipeline(); - return NULL; + ShaderProgramVulkan* shader = new ShaderProgramVulkan(); + + CCFeatures cc_features; + gfx_cc_get_features(shader_id, &cc_features); + + auto [pipeline, layout] = gfx_vulkan_create_pipeline(cc_features); + shader->pipeline = pipeline; + shader->pipeline_layout = layout; + + cached_shaders[shader_id] = shader; + + gfx_vulkan_renderer_load_shader(reinterpret_cast(shader)); + + return reinterpret_cast(shader); } static struct ShaderProgram *gfx_vulkan_renderer_lookup_shader(uint32_t shader_id) { @@ -755,14 +965,23 @@ static void gfx_vulkan_renderer_set_use_alpha(bool use_alpha) { } static void gfx_vulkan_renderer_unload_shader(struct ShaderProgram *old_prg) { + } static void gfx_vulkan_renderer_load_shader(struct ShaderProgram *new_prg) { + ShaderProgramVulkan* vulkan = (ShaderProgramVulkan*)new_prg; + + if(current_cmd != VK_NULL_HANDLE) { + vkCmdBindPipeline(current_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan->pipeline); + } + + last_program = vulkan; } static void gfx_vulkan_renderer_draw_triangles(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris) { - // FIXME: uh no - vkCmdBindPipeline(current_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + if(last_program != nullptr) { + gfx_vulkan_renderer_load_shader(reinterpret_cast(last_program)); + } void* mapped_data = nullptr; vkMapMemory(device, vertex_memory, 0, buf_vbo_len, 0, &mapped_data); @@ -827,6 +1046,7 @@ static void gfx_vulkan_renderer_finish_render(void) { vkQueuePresentKHR(present_queue, &present_info); current_frame = (current_frame + 1) % 3; + current_cmd = VK_NULL_HANDLE; } static const char* gfx_vulkan_get_name() {