xash3d-fwgs/ref/vk/vk_pipeline.c
Ivan Avdeev 4bd62ccbc0 [draft] vk: start refactoring commandbuffer/gpu profiler
Consolidate VkCommandBuffer management into a single entity. (somewhat
done for framectl, not done for staging)

Make sure that we pass enough metadata to be able to timestamp scopes in
cmdbuf.

It does compile, but it won't work: not all init code paths are used.
Also, there are many changes, so other failure modes are totally
possible.
2023-04-10 10:24:57 -07:00

379 lines
13 KiB
C

#include "vk_pipeline.h"
#include "vk_framectl.h" // VkRenderPass
#include "vk_combuf.h"
#include "eiface.h"
#define MAX_STAGES 2
VkPipelineCache g_pipeline_cache;
qboolean VK_PipelineInit( void )
{
VkPipelineCacheCreateInfo pcci = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
.initialDataSize = 0,
.pInitialData = NULL,
};
XVK_CHECK(vkCreatePipelineCache(vk_core.device, &pcci, NULL, &g_pipeline_cache));
return true;
}
void VK_PipelineShutdown( void )
{
vkDestroyPipelineCache(vk_core.device, g_pipeline_cache, NULL);
}
VkShaderModule R_VkShaderLoadFromMem(const void *ptr, uint32_t size, const char *name) {
if ((size % 4 != 0) || (((uintptr_t)ptr & 3) != 0)) {
gEngine.Con_Printf(S_ERROR "Couldn't load shader %s: size %u or buf %p is not aligned to 4 bytes as required by SPIR-V/Vulkan spec\n", name, size, ptr);
return VK_NULL_HANDLE;
}
const VkShaderModuleCreateInfo smci = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = size,
.pCode = (const uint32_t*)(void*)ptr,
};
VkShaderModule module = VK_NULL_HANDLE;
const VkResult result = vkCreateShaderModule(vk_core.device, &smci, NULL, &module);
if (result != VK_SUCCESS) {
gEngine.Con_Printf(S_ERROR "Couldn't load shader %s: error (%d): %s\n", name, result, R_VkResultName(result));
return VK_NULL_HANDLE;
}
SET_DEBUG_NAME(module, VK_OBJECT_TYPE_SHADER_MODULE, name);
return module;
}
static VkShaderModule R_VkShaderLoadFromFile(const char *filename) {
fs_offset_t size = 0;
byte* const buf = gEngine.fsapi->LoadFile(filename, &size, false);
if (!buf) {
gEngine.Con_Printf( S_ERROR "Cannot open shader file \"%s\"\n", filename);
return VK_NULL_HANDLE;
}
const VkShaderModule module = R_VkShaderLoadFromMem(buf, size, filename);
finalize:
Mem_Free(buf);
return module;
}
void R_VkShaderDestroy(VkShaderModule module) {
vkDestroyShaderModule(vk_core.device, module, NULL);
}
VkPipeline VK_PipelineGraphicsCreate(const vk_pipeline_graphics_create_info_t *ci)
{
VkPipeline pipeline;
VkVertexInputBindingDescription vibd = {
.binding = 0,
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
.stride = ci->vertex_stride,
};
VkPipelineVertexInputStateCreateInfo vertex_input = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = &vibd,
.vertexAttributeDescriptionCount = ci->num_attribs,
.pVertexAttributeDescriptions = ci->attribs,
};
VkPipelineInputAssemblyStateCreateInfo input_assembly = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
};
VkPipelineViewportStateCreateInfo viewport_state = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.scissorCount = 1,
};
VkPipelineRasterizationStateCreateInfo raster_state = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.polygonMode = VK_POLYGON_MODE_FILL,
.cullMode = ci->cullMode,
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
.lineWidth = 1.f,
};
VkPipelineMultisampleStateCreateInfo multi_state = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
};
VkPipelineColorBlendAttachmentState blend_attachment = {
.blendEnable = ci->blendEnable,
.srcColorBlendFactor = ci->srcColorBlendFactor,
.dstColorBlendFactor = ci->dstColorBlendFactor,
.colorBlendOp = ci->colorBlendOp,
.srcAlphaBlendFactor = ci->srcAlphaBlendFactor,
.dstAlphaBlendFactor = ci->dstAlphaBlendFactor,
.alphaBlendOp = ci->alphaBlendOp,
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
};
VkPipelineColorBlendStateCreateInfo color_blend = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = &blend_attachment,
};
VkPipelineDepthStencilStateCreateInfo depth = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = ci->depthTestEnable,
.depthWriteEnable = ci->depthWriteEnable,
.depthCompareOp = ci->depthCompareOp,
};
VkDynamicState dynamic_states[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
VkPipelineDynamicStateCreateInfo dynamic_state_create_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = ARRAYSIZE(dynamic_states),
.pDynamicStates = dynamic_states,
};
VkPipelineShaderStageCreateInfo stage_create_infos[MAX_STAGES];
VkGraphicsPipelineCreateInfo gpci = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = ci->num_stages,
.pStages = stage_create_infos,
.pVertexInputState = &vertex_input,
.pInputAssemblyState = &input_assembly,
.pViewportState = &viewport_state,
.pRasterizationState = &raster_state,
.pMultisampleState = &multi_state,
.pColorBlendState = &color_blend,
.pDepthStencilState = &depth,
.layout = ci->layout,
.renderPass = vk_frame.render_pass.raster,
.pDynamicState = &dynamic_state_create_info,
.subpass = 0,
};
if (ci->num_stages > MAX_STAGES)
return VK_NULL_HANDLE;
VkShaderModule shaders[MAX_STAGES] = {VK_NULL_HANDLE};
for (int i = 0; i < ci->num_stages; ++i) {
if (VK_NULL_HANDLE == (shaders[i] = R_VkShaderLoadFromFile(ci->stages[i].filename)))
goto finalize;
stage_create_infos[i] = (VkPipelineShaderStageCreateInfo){
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = ci->stages[i].stage,
.module = shaders[i],
.pSpecializationInfo = ci->stages[i].specialization_info,
.pName = "main",
};
}
XVK_CHECK(vkCreateGraphicsPipelines(vk_core.device, g_pipeline_cache, 1, &gpci, NULL, &pipeline));
finalize:
for (int i = 0; i < ci->num_stages; ++i)
R_VkShaderDestroy(shaders[i]);
return pipeline;
}
VkPipeline VK_PipelineComputeCreate(const vk_pipeline_compute_create_info_t *ci) {
const VkComputePipelineCreateInfo cpci = {
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
.layout = ci->layout,
.stage = (VkPipelineShaderStageCreateInfo){
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_COMPUTE_BIT,
.module = ci->shader_module,
.pName = "main",
.pSpecializationInfo = ci->specialization_info,
},
};
VkPipeline pipeline;
XVK_CHECK(vkCreateComputePipelines(vk_core.device, VK_NULL_HANDLE, 1, &cpci, NULL, &pipeline));
return pipeline;
}
vk_pipeline_ray_t VK_PipelineRayTracingCreate(const vk_pipeline_ray_create_info_t *create) {
#define MAX_SHADER_STAGES 16
#define MAX_SHADER_GROUPS 16
vk_pipeline_ray_t ret = {0};
VkPipelineShaderStageCreateInfo stages[MAX_SHADER_STAGES];
VkRayTracingShaderGroupCreateInfoKHR shader_groups[MAX_SHADER_GROUPS];
const int shader_groups_count = create->groups.hit_count + create->groups.miss_count + 1;
int raygen_index = -1;
int group_index = 0;
const VkRayTracingPipelineCreateInfoKHR rtpci = {
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR,
//TODO .flags = VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_ANY_HIT_SHADERS_BIT_KHR ....
.stageCount = create->stages_count,
.pStages = stages,
.groupCount = shader_groups_count,
.pGroups = shader_groups,
.maxPipelineRayRecursionDepth = 1,
.layout = create->layout,
};
ASSERT(shader_groups_count <= MAX_SHADER_GROUPS);
if (create->stages_count > MAX_SHADER_STAGES) {
gEngine.Con_Printf(S_ERROR "Too many shader stages %d, max=%d\n", create->stages_count, MAX_SHADER_STAGES);
return ret;
}
for (int i = 0; i < create->stages_count; ++i) {
const vk_shader_stage_t *const stage = create->stages + i;
// FIXME going away from loading shaders directly
ASSERT(!stage->filename);
if (stage->stage == VK_SHADER_STAGE_RAYGEN_BIT_KHR) {
ASSERT(raygen_index == -1);
raygen_index = i;
}
stages[i] = (VkPipelineShaderStageCreateInfo){
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = stage->stage,
.module = stage->module,
.pName = "main",
.pSpecializationInfo = stage->specialization_info,
};
}
ASSERT(raygen_index >= 0);
shader_groups[group_index++] = (VkRayTracingShaderGroupCreateInfoKHR) {
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR,
.anyHitShader = VK_SHADER_UNUSED_KHR,
.closestHitShader = VK_SHADER_UNUSED_KHR,
.generalShader = raygen_index,
.intersectionShader = VK_SHADER_UNUSED_KHR,
};
for (int i = 0; i < create->groups.miss_count; ++i) {
const int miss_index = create->groups.miss[i];
ASSERT(miss_index >= 0);
ASSERT(miss_index < create->stages_count);
ASSERT(create->stages[miss_index].stage == VK_SHADER_STAGE_MISS_BIT_KHR);
shader_groups[group_index++] = (VkRayTracingShaderGroupCreateInfoKHR) {
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR,
.anyHitShader = VK_SHADER_UNUSED_KHR,
.closestHitShader = VK_SHADER_UNUSED_KHR,
.generalShader = miss_index,
.intersectionShader = VK_SHADER_UNUSED_KHR,
};
}
for (int i = 0; i < create->groups.hit_count; ++i) {
const vk_pipeline_ray_hit_group_t *const groups = create->groups.hit + i;
const int closest_index = groups->closest >= 0 ? groups->closest : VK_SHADER_UNUSED_KHR;
const int any_index = groups->any >= 0 ? groups->any : VK_SHADER_UNUSED_KHR;
if (closest_index != VK_SHADER_UNUSED_KHR) {
ASSERT(closest_index < create->stages_count);
ASSERT(create->stages[closest_index].stage == VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR);
}
if (any_index != VK_SHADER_UNUSED_KHR) {
ASSERT(any_index < create->stages_count);
ASSERT(create->stages[any_index].stage == VK_SHADER_STAGE_ANY_HIT_BIT_KHR);
}
shader_groups[group_index++] = (VkRayTracingShaderGroupCreateInfoKHR) {
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR,
.anyHitShader = any_index,
.closestHitShader = closest_index,
.generalShader = VK_SHADER_UNUSED_KHR,
.intersectionShader = VK_SHADER_UNUSED_KHR,
};
}
XVK_CHECK(vkCreateRayTracingPipelinesKHR(vk_core.device, VK_NULL_HANDLE, g_pipeline_cache, 1, &rtpci, NULL, &ret.pipeline));
if (ret.pipeline == VK_NULL_HANDLE)
return ret;
// TODO: do not allocate sbt buffer per pipeline. make a central buffer and use that
// TODO: does it really need to be host-visible?
{
char buf[64];
Q_snprintf(buf, sizeof(buf), "%s sbt", create->debug_name);
if (!VK_BufferCreate(buf, &ret.sbt_buffer, shader_groups_count * vk_core.physical_device.sbt_record_size,
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
{
vkDestroyPipeline(vk_core.device, ret.pipeline, NULL);
ret.pipeline = VK_NULL_HANDLE;
return ret;
}
}
{
const uint32_t sbt_handle_size = vk_core.physical_device.properties_ray_tracing_pipeline.shaderGroupHandleSize;
const uint32_t sbt_handles_buffer_size = shader_groups_count * sbt_handle_size;
uint8_t *sbt_handles = Mem_Malloc(vk_core.pool, sbt_handles_buffer_size);
XVK_CHECK(vkGetRayTracingShaderGroupHandlesKHR(vk_core.device, ret.pipeline, 0, shader_groups_count, sbt_handles_buffer_size, sbt_handles));
for (int i = 0; i < shader_groups_count; ++i)
{
uint8_t *sbt_dst = ret.sbt_buffer.mapped;
memcpy(sbt_dst + vk_core.physical_device.sbt_record_size * i, sbt_handles + sbt_handle_size * i, sbt_handle_size);
}
Mem_Free(sbt_handles);
}
{
const VkDeviceAddress sbt_addr = R_VkBufferGetDeviceAddress(ret.sbt_buffer.buffer);
const uint32_t sbt_record_size = vk_core.physical_device.sbt_record_size;
uint32_t index = 0;
#define SBT_INDEX(count) (VkStridedDeviceAddressRegionKHR){ \
.deviceAddress = sbt_addr + sbt_record_size * index, \
.size = sbt_record_size * (count), \
.stride = sbt_record_size, \
}; index += count
ret.sbt.raygen = SBT_INDEX(1);
ret.sbt.miss = SBT_INDEX(create->groups.miss_count);
ret.sbt.hit = SBT_INDEX(create->groups.hit_count);
ret.sbt.callable = (VkStridedDeviceAddressRegionKHR){ 0 };
}
Q_strncpy(ret.debug_name, create->debug_name, sizeof(ret.debug_name));
return ret;
}
void VK_PipelineRayTracingDestroy(vk_pipeline_ray_t* pipeline) {
vkDestroyPipeline(vk_core.device, pipeline->pipeline, NULL);
VK_BufferDestroy(&pipeline->sbt_buffer);
pipeline->pipeline = VK_NULL_HANDLE;
}
void VK_PipelineRayTracingTrace(vk_combuf_t *combuf, const vk_pipeline_ray_t *pipeline, uint32_t width, uint32_t height, int scope_id) {
// TODO bind this and accepts descriptors as args? vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline->pipeline);
const int begin_id = R_VkCombufScopeBegin(combuf, scope_id);
vkCmdTraceRaysKHR(combuf->cmdbuf, &pipeline->sbt.raygen, &pipeline->sbt.miss, &pipeline->sbt.hit, &pipeline->sbt.callable, width, height, 1 );
R_VkCombufScopeEnd(combuf, begin_id, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR);
}