1
Fork 0

Dynamically compile glsl shaders on the fly

This also begins on proper shader support, but it's not there yet.
This commit is contained in:
Joshua Goins 2022-10-05 09:44:17 -04:00
parent 2807cf2847
commit 9f184efaf2
3 changed files with 348 additions and 19 deletions

View file

@ -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

109
src/pc/gfx/defaultresources.hpp Executable file
View file

@ -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,
}};

View file

@ -7,11 +7,14 @@
#include <array>
#include <limits>
#include <fstream>
#include <glslang/Public/ShaderLang.h>
#include <glslang/SPIRV/GlslangToSpv.h>
#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<VkSemaphore, 3> render_finished;
static std::array<VkFence, 3> 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<uint32_t, ShaderProgramVulkan*> 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<const uint32_t *>(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<unsigned int> 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<VkPipeline, VkPipelineLayout> 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<VkPipelineShaderStageCreateInfo, 2> 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<VkVertexInputAttributeDescription> 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<ShaderProgram *>(shader));
return reinterpret_cast<ShaderProgram *>(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<ShaderProgram *>(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() {