mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-25 13:03:50 +00:00
M2 GPU instancing - M2InstanceGPU SSBO (96 B/entry, double-buffered, 16384 max) - Group opaque instances by (modelId, LOD); single vkCmdDrawIndexed per group - boneBase field indexes into mega bone SSBO via gl_InstanceIndex Indirect terrain drawing - 24 MB mega index buffer (6M uint32) + 64 MB mega vertex buffer - CPU builds VkDrawIndexedIndirectCommand per visible chunk - Single VB/IB bind per frame; shadow pass reuses mega buffers - Replaced vkCmdDrawIndexedIndirect with direct vkCmdDrawIndexed to fix host-mapped buffer race condition that caused terrain flickering GPU frustum culling (compute shader) - m2_cull.comp.glsl: 64-thread workgroups, sphere-vs-6-planes + distance cull - CullInstanceGPU SSBO input, uint visibility[] output, double-buffered - dispatchCullCompute() runs before main pass via render graph node Consolidated bone matrix SSBOs - 16 MB double-buffered mega bone SSBO (2048 instances × 128 bones) - Eliminated per-instance descriptor sets; one megaBoneSet_ per frame - prepareRender() packs bone matrices consecutively into current frame slot Render graph / frame graph - RenderGraph: RGResource handles, RGPass nodes, Kahn topological sort - Automatic VkImageMemoryBarrier/VkBufferMemoryBarrier between passes - Passes: minimap_composite, worldmap_composite, preview_composite, shadow_pass, reflection_pass, compute_cull - beginFrame() uses buildFrameGraph() + renderGraph_->execute(cmd) Pipeline derivatives - PipelineBuilder::setFlags/setBasePipeline for VK_PIPELINE_CREATE_DERIVATIVE_BIT - M2 opaque = base; alphaTest/alpha/additive are derivatives - Applied to terrain (wireframe) and WMO (alpha-test) renderers Rendering bug fixes: - fix(shadow): compute lightSpaceMatrix before updatePerFrameUBO to eliminate one-frame lag that caused shadow trails and flicker on moving objects - fix(shadow): scale depth bias with shadowDistance_ instead of hardcoded 0.8f to prevent acne at close range and gaps at far range - fix(visibility): WMO group distance threshold 500u → 1200u to match terrain view distance; buildings were disappearing on the horizon - fix(precision): camera near plane 0.05 → 0.5 (ratio 600K:1 → 60K:1), eliminating Z-fighting and improving frustum plane extraction stability - fix(streaming): terrain load radius 4 → 6 tiles (~2133u → ~3200u) to exceed M2 render distance (2800u) and eliminate pop-in when camera turns; unload radius 7 → 9; spawn radius 3 → 4 - fix(visibility): ground-detail M2 distance multiplier 0.75 → 0.9 to reduce early pop of grass and debris
307 lines
11 KiB
C++
307 lines
11 KiB
C++
#include "rendering/vk_pipeline.hpp"
|
|
#include "core/logger.hpp"
|
|
|
|
namespace wowee {
|
|
namespace rendering {
|
|
|
|
PipelineBuilder::PipelineBuilder() {
|
|
// Default: one blend attachment with blending disabled
|
|
colorBlendAttachments_.push_back(blendDisabled());
|
|
|
|
// Default dynamic states: viewport + scissor (almost always dynamic)
|
|
dynamicStates_ = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
|
|
}
|
|
|
|
PipelineBuilder& PipelineBuilder::setShaders(
|
|
VkPipelineShaderStageCreateInfo vert, VkPipelineShaderStageCreateInfo frag)
|
|
{
|
|
shaderStages_ = {vert, frag};
|
|
return *this;
|
|
}
|
|
|
|
PipelineBuilder& PipelineBuilder::setVertexInput(
|
|
const std::vector<VkVertexInputBindingDescription>& bindings,
|
|
const std::vector<VkVertexInputAttributeDescription>& attributes)
|
|
{
|
|
vertexBindings_ = bindings;
|
|
vertexAttributes_ = attributes;
|
|
return *this;
|
|
}
|
|
|
|
PipelineBuilder& PipelineBuilder::setNoVertexInput() {
|
|
vertexBindings_.clear();
|
|
vertexAttributes_.clear();
|
|
return *this;
|
|
}
|
|
|
|
PipelineBuilder& PipelineBuilder::setTopology(VkPrimitiveTopology topology,
|
|
VkBool32 primitiveRestart)
|
|
{
|
|
topology_ = topology;
|
|
primitiveRestart_ = primitiveRestart;
|
|
return *this;
|
|
}
|
|
|
|
PipelineBuilder& PipelineBuilder::setRasterization(VkPolygonMode polygonMode,
|
|
VkCullModeFlags cullMode, VkFrontFace frontFace)
|
|
{
|
|
polygonMode_ = polygonMode;
|
|
cullMode_ = cullMode;
|
|
frontFace_ = frontFace;
|
|
return *this;
|
|
}
|
|
|
|
PipelineBuilder& PipelineBuilder::setDepthTest(bool enable, bool writeEnable,
|
|
VkCompareOp compareOp)
|
|
{
|
|
depthTestEnable_ = enable;
|
|
depthWriteEnable_ = writeEnable;
|
|
depthCompareOp_ = compareOp;
|
|
return *this;
|
|
}
|
|
|
|
PipelineBuilder& PipelineBuilder::setNoDepthTest() {
|
|
depthTestEnable_ = false;
|
|
depthWriteEnable_ = false;
|
|
return *this;
|
|
}
|
|
|
|
PipelineBuilder& PipelineBuilder::setDepthBias(float constantFactor, float slopeFactor) {
|
|
depthBiasEnable_ = true;
|
|
depthBiasConstant_ = constantFactor;
|
|
depthBiasSlope_ = slopeFactor;
|
|
return *this;
|
|
}
|
|
|
|
PipelineBuilder& PipelineBuilder::setColorBlendAttachment(
|
|
VkPipelineColorBlendAttachmentState blendState)
|
|
{
|
|
colorBlendAttachments_ = {blendState};
|
|
return *this;
|
|
}
|
|
|
|
PipelineBuilder& PipelineBuilder::setNoColorAttachment() {
|
|
colorBlendAttachments_.clear();
|
|
return *this;
|
|
}
|
|
|
|
PipelineBuilder& PipelineBuilder::setMultisample(VkSampleCountFlagBits samples) {
|
|
msaaSamples_ = samples;
|
|
return *this;
|
|
}
|
|
|
|
PipelineBuilder& PipelineBuilder::setAlphaToCoverage(bool enable) {
|
|
alphaToCoverage_ = enable;
|
|
return *this;
|
|
}
|
|
|
|
PipelineBuilder& PipelineBuilder::setLayout(VkPipelineLayout layout) {
|
|
pipelineLayout_ = layout;
|
|
return *this;
|
|
}
|
|
|
|
PipelineBuilder& PipelineBuilder::setRenderPass(VkRenderPass renderPass, uint32_t subpass) {
|
|
renderPass_ = renderPass;
|
|
subpass_ = subpass;
|
|
return *this;
|
|
}
|
|
|
|
PipelineBuilder& PipelineBuilder::setDynamicStates(const std::vector<VkDynamicState>& states) {
|
|
dynamicStates_ = states;
|
|
return *this;
|
|
}
|
|
|
|
// Pipeline derivatives — hint driver to share compiled state between similar pipelines
|
|
PipelineBuilder& PipelineBuilder::setFlags(VkPipelineCreateFlags flags) {
|
|
flags_ = flags;
|
|
return *this;
|
|
}
|
|
|
|
PipelineBuilder& PipelineBuilder::setBasePipeline(VkPipeline basePipeline) {
|
|
basePipelineHandle_ = basePipeline;
|
|
return *this;
|
|
}
|
|
|
|
VkPipeline PipelineBuilder::build(VkDevice device, VkPipelineCache cache) const {
|
|
// Vertex input
|
|
VkPipelineVertexInputStateCreateInfo vertexInput{};
|
|
vertexInput.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
|
vertexInput.vertexBindingDescriptionCount = static_cast<uint32_t>(vertexBindings_.size());
|
|
vertexInput.pVertexBindingDescriptions = vertexBindings_.data();
|
|
vertexInput.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexAttributes_.size());
|
|
vertexInput.pVertexAttributeDescriptions = vertexAttributes_.data();
|
|
|
|
// Input assembly
|
|
VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
|
|
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
|
inputAssembly.topology = topology_;
|
|
inputAssembly.primitiveRestartEnable = primitiveRestart_;
|
|
|
|
// Viewport / scissor (dynamic, so just specify count)
|
|
VkPipelineViewportStateCreateInfo viewportState{};
|
|
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
|
viewportState.viewportCount = 1;
|
|
viewportState.scissorCount = 1;
|
|
|
|
// Rasterization
|
|
VkPipelineRasterizationStateCreateInfo rasterizer{};
|
|
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
|
rasterizer.depthClampEnable = VK_FALSE;
|
|
rasterizer.rasterizerDiscardEnable = VK_FALSE;
|
|
rasterizer.polygonMode = polygonMode_;
|
|
rasterizer.lineWidth = 1.0f;
|
|
rasterizer.cullMode = cullMode_;
|
|
rasterizer.frontFace = frontFace_;
|
|
rasterizer.depthBiasEnable = depthBiasEnable_ ? VK_TRUE : VK_FALSE;
|
|
rasterizer.depthBiasConstantFactor = depthBiasConstant_;
|
|
rasterizer.depthBiasSlopeFactor = depthBiasSlope_;
|
|
|
|
// Multisampling
|
|
VkPipelineMultisampleStateCreateInfo multisampling{};
|
|
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
|
multisampling.sampleShadingEnable = VK_FALSE;
|
|
multisampling.rasterizationSamples = msaaSamples_;
|
|
multisampling.alphaToCoverageEnable = alphaToCoverage_ ? VK_TRUE : VK_FALSE;
|
|
|
|
// Depth/stencil
|
|
VkPipelineDepthStencilStateCreateInfo depthStencil{};
|
|
depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
|
depthStencil.depthTestEnable = depthTestEnable_ ? VK_TRUE : VK_FALSE;
|
|
depthStencil.depthWriteEnable = depthWriteEnable_ ? VK_TRUE : VK_FALSE;
|
|
depthStencil.depthCompareOp = depthCompareOp_;
|
|
depthStencil.depthBoundsTestEnable = VK_FALSE;
|
|
depthStencil.stencilTestEnable = VK_FALSE;
|
|
|
|
// Color blending
|
|
VkPipelineColorBlendStateCreateInfo colorBlending{};
|
|
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
|
colorBlending.logicOpEnable = VK_FALSE;
|
|
colorBlending.attachmentCount = static_cast<uint32_t>(colorBlendAttachments_.size());
|
|
colorBlending.pAttachments = colorBlendAttachments_.data();
|
|
|
|
// Dynamic state
|
|
VkPipelineDynamicStateCreateInfo dynamicState{};
|
|
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
|
dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates_.size());
|
|
dynamicState.pDynamicStates = dynamicStates_.data();
|
|
|
|
// Create pipeline
|
|
VkGraphicsPipelineCreateInfo pipelineInfo{};
|
|
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
|
pipelineInfo.stageCount = static_cast<uint32_t>(shaderStages_.size());
|
|
pipelineInfo.pStages = shaderStages_.data();
|
|
pipelineInfo.pVertexInputState = &vertexInput;
|
|
pipelineInfo.pInputAssemblyState = &inputAssembly;
|
|
pipelineInfo.pViewportState = &viewportState;
|
|
pipelineInfo.pRasterizationState = &rasterizer;
|
|
pipelineInfo.pMultisampleState = &multisampling;
|
|
pipelineInfo.pDepthStencilState = &depthStencil;
|
|
pipelineInfo.pColorBlendState = colorBlendAttachments_.empty() ? nullptr : &colorBlending;
|
|
pipelineInfo.pDynamicState = dynamicStates_.empty() ? nullptr : &dynamicState;
|
|
pipelineInfo.layout = pipelineLayout_;
|
|
pipelineInfo.flags = flags_;
|
|
pipelineInfo.basePipelineHandle = basePipelineHandle_;
|
|
pipelineInfo.basePipelineIndex = -1;
|
|
pipelineInfo.renderPass = renderPass_;
|
|
pipelineInfo.subpass = subpass_;
|
|
|
|
VkPipeline pipeline = VK_NULL_HANDLE;
|
|
if (vkCreateGraphicsPipelines(device, cache, 1, &pipelineInfo,
|
|
nullptr, &pipeline) != VK_SUCCESS)
|
|
{
|
|
LOG_ERROR("Failed to create graphics pipeline");
|
|
return VK_NULL_HANDLE;
|
|
}
|
|
|
|
return pipeline;
|
|
}
|
|
|
|
// All RGBA channels enabled — used by every blend mode since we never need to
|
|
// mask individual channels (WoW's fixed-function pipeline always writes all four).
|
|
static constexpr VkColorComponentFlags kColorWriteAll =
|
|
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
|
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
|
|
|
VkPipelineColorBlendAttachmentState PipelineBuilder::blendDisabled() {
|
|
VkPipelineColorBlendAttachmentState state{};
|
|
state.colorWriteMask = kColorWriteAll;
|
|
state.blendEnable = VK_FALSE;
|
|
return state;
|
|
}
|
|
|
|
VkPipelineColorBlendAttachmentState PipelineBuilder::blendAlpha() {
|
|
VkPipelineColorBlendAttachmentState state{};
|
|
state.colorWriteMask = kColorWriteAll;
|
|
state.blendEnable = VK_TRUE;
|
|
state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
|
state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
|
state.colorBlendOp = VK_BLEND_OP_ADD;
|
|
state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
|
state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
|
state.alphaBlendOp = VK_BLEND_OP_ADD;
|
|
return state;
|
|
}
|
|
|
|
VkPipelineColorBlendAttachmentState PipelineBuilder::blendPremultiplied() {
|
|
VkPipelineColorBlendAttachmentState state{};
|
|
state.colorWriteMask = kColorWriteAll;
|
|
state.blendEnable = VK_TRUE;
|
|
state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
|
state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
|
state.colorBlendOp = VK_BLEND_OP_ADD;
|
|
state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
|
state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
|
state.alphaBlendOp = VK_BLEND_OP_ADD;
|
|
return state;
|
|
}
|
|
|
|
VkPipelineColorBlendAttachmentState PipelineBuilder::blendAdditive() {
|
|
VkPipelineColorBlendAttachmentState state{};
|
|
state.colorWriteMask = kColorWriteAll;
|
|
state.blendEnable = VK_TRUE;
|
|
state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
|
state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
|
state.colorBlendOp = VK_BLEND_OP_ADD;
|
|
state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
|
state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
|
state.alphaBlendOp = VK_BLEND_OP_ADD;
|
|
return state;
|
|
}
|
|
|
|
VkPipelineLayout createPipelineLayout(VkDevice device,
|
|
const std::vector<VkDescriptorSetLayout>& setLayouts,
|
|
const std::vector<VkPushConstantRange>& pushConstants)
|
|
{
|
|
VkPipelineLayoutCreateInfo layoutInfo{};
|
|
layoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
|
layoutInfo.setLayoutCount = static_cast<uint32_t>(setLayouts.size());
|
|
layoutInfo.pSetLayouts = setLayouts.data();
|
|
layoutInfo.pushConstantRangeCount = static_cast<uint32_t>(pushConstants.size());
|
|
layoutInfo.pPushConstantRanges = pushConstants.data();
|
|
|
|
VkPipelineLayout layout = VK_NULL_HANDLE;
|
|
if (vkCreatePipelineLayout(device, &layoutInfo, nullptr, &layout) != VK_SUCCESS) {
|
|
LOG_ERROR("Failed to create pipeline layout");
|
|
}
|
|
|
|
return layout;
|
|
}
|
|
|
|
VkDescriptorSetLayout createDescriptorSetLayout(VkDevice device,
|
|
const std::vector<VkDescriptorSetLayoutBinding>& bindings)
|
|
{
|
|
VkDescriptorSetLayoutCreateInfo layoutInfo{};
|
|
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
|
layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
|
|
layoutInfo.pBindings = bindings.data();
|
|
|
|
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
|
|
if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &layout) != VK_SUCCESS) {
|
|
LOG_ERROR("Failed to create descriptor set layout");
|
|
}
|
|
|
|
return layout;
|
|
}
|
|
|
|
} // namespace rendering
|
|
} // namespace wowee
|