From bd63b14260a2d4e8d2fee9373ccf4390ec03eefe Mon Sep 17 00:00:00 2001 From: Dynamitos Date: Thu, 20 Jun 2024 21:57:26 +0200 Subject: [PATCH] Adding transparency support --- res/shaders/DepthCullingTask.slang | 110 +++--- res/shaders/DepthMipGen.slang | 2 +- src/Editor/Asset/MaterialLoader.cpp | 2 +- src/Editor/Asset/MeshLoader.cpp | 12 +- src/Editor/main.cpp | 13 +- src/Engine/Component/Camera.cpp | 1 + src/Engine/Component/Camera.h | 2 + src/Engine/Graphics/Descriptor.h | 4 +- src/Engine/Graphics/Initializer.h | 7 +- src/Engine/Graphics/RenderPass/BasePass.cpp | 116 +++++- src/Engine/Graphics/RenderPass/BasePass.h | 4 + .../Graphics/RenderPass/DepthCullingPass.cpp | 4 +- src/Engine/Graphics/RenderPass/TextPass.cpp | 2 +- src/Engine/Graphics/RenderPass/UIPass.h | 2 +- .../Graphics/RenderPass/VisibilityPass.cpp | 1 + src/Engine/Graphics/VertexData.cpp | 363 ++++++++++-------- src/Engine/Graphics/VertexData.h | 18 + src/Engine/Graphics/Vulkan/Buffer.cpp | 21 +- src/Engine/Graphics/Vulkan/Buffer.h | 5 +- src/Engine/Graphics/Vulkan/Command.cpp | 34 +- src/Engine/Graphics/Vulkan/Descriptor.cpp | 71 +++- src/Engine/Graphics/Vulkan/Descriptor.h | 5 +- src/Engine/Graphics/Vulkan/Query.cpp | 7 +- src/Engine/Graphics/Vulkan/Query.h | 2 + src/Engine/Graphics/Vulkan/Resources.cpp | 7 +- src/Engine/Material/Material.cpp | 35 +- src/Engine/Material/Material.h | 10 +- 27 files changed, 578 insertions(+), 282 deletions(-) diff --git a/res/shaders/DepthCullingTask.slang b/res/shaders/DepthCullingTask.slang index 95dd7eb..2e727d6 100644 --- a/res/shaders/DepthCullingTask.slang +++ b/res/shaders/DepthCullingTask.slang @@ -1,5 +1,6 @@ import Common; import Scene; +import Bounding; groupshared MeshPayload p; groupshared uint head; @@ -7,6 +8,7 @@ groupshared MeshData mesh; groupshared InstanceData instance; groupshared Frustum viewFrustum; groupshared float4x4 modelViewProjection; +groupshared bool meshVisible; struct DepthData { @@ -16,6 +18,60 @@ struct DepthData ParameterBlock pDepthAttachment; +bool isBoxVisible(AABB bounding) +{ + uint2 mipDimensions = uint2((uint(pViewParams.screenDimensions.x) + BLOCK_SIZE - 1) / BLOCK_SIZE, (uint(pViewParams.screenDimensions.y) + BLOCK_SIZE - 1) / BLOCK_SIZE); + // now we calculate what mip level we need to only sample up to 4 texels covering the entire meshlet + uint2 screenCornerMin = mipDimensions; + uint2 screenCornerMax = uint2(0, 0); + // we use reverse depth, so higher values are closer + float maxDepth = 0; + { + float4 corners[8]; + corners[0] = float4(bounding.min.x, bounding.min.y, bounding.min.z, 1.0f); + corners[1] = float4(bounding.min.x, bounding.min.y, bounding.max.z, 1.0f); + corners[2] = float4(bounding.min.x, bounding.max.y, bounding.min.z, 1.0f); + corners[3] = float4(bounding.min.x, bounding.max.y, bounding.max.z, 1.0f); + corners[4] = float4(bounding.max.x, bounding.min.y, bounding.min.z, 1.0f); + corners[5] = float4(bounding.max.x, bounding.min.y, bounding.max.z, 1.0f); + corners[6] = float4(bounding.max.x, bounding.max.y, bounding.min.z, 1.0f); + corners[7] = float4(bounding.max.x, bounding.max.y, bounding.max.z, 1.0f); + for(uint i = 0; i < 8; ++i) + { + float4 clipCorner = mul(modelViewProjection, corners[i]); + float4 screenCorner = clipToScreen(clipCorner) / BLOCK_SIZE; + screenCornerMin = uint2(min(screenCornerMin.x, uint(screenCorner.x)), min(screenCornerMin.y, uint(screenCorner.y))); + screenCornerMax = uint2(max(screenCornerMax.x, uint(screenCorner.x)), max(screenCornerMax.y, uint(screenCorner.y))); + maxDepth = max(maxDepth, screenCorner.z); + } + } + uint mipOffset = 0; + // in theory this wouldnt work if no corner was in screen, as min would be greater that max, however we verified that with view culling + while(screenCornerMax.x - screenCornerMin.x > 1 || screenCornerMax.y - screenCornerMin.y > 1) + { + mipOffset += mipDimensions.x * mipDimensions.y; + mipDimensions = uint2(mipDimensions.x + 1, mipDimensions.y + 1) / 2; + screenCornerMin /= 2; + screenCornerMax /= 2; + } + + // now we sample 4 texels from the depth at the calculated mip level, this should give us the screen extent of the meshlet + float d1 = pDepthAttachment.buffer[mipOffset + (screenCornerMin.y * mipDimensions.x) + screenCornerMin.x]; + float d2 = pDepthAttachment.buffer[mipOffset + (screenCornerMin.y * mipDimensions.x) + screenCornerMax.x]; + float d3 = pDepthAttachment.buffer[mipOffset + (screenCornerMax.y * mipDimensions.x) + screenCornerMin.x]; + float d4 = pDepthAttachment.buffer[mipOffset + (screenCornerMax.y * mipDimensions.x) + screenCornerMax.x]; + + // we want to check if the minimum depth (the value farthest away) is smaller than the maximum bounding box depth + // otherwise, there is no way for the meshlet to be visible + float d = min(min(d1, d2), min(d3, d4)); + + if(d < maxDepth) + { + return true; + } + return false; +} + [numthreads(TASK_GROUP_SIZE, 1, 1)] [shader("amplification")] void taskMain( @@ -43,8 +99,13 @@ void taskMain( viewFrustum.sides[1] = computePlane(origin, corners[1], corners[3]); viewFrustum.sides[2] = computePlane(origin, corners[0], corners[1]); viewFrustum.sides[3] = computePlane(origin, corners[3], corners[2]); + meshVisible = isBoxVisible(mesh.bounding); } GroupMemoryBarrierWithGroupSync(); + if(!meshVisible) + { + return; + } for (uint i = threadID; i < mesh.numMeshlets; i += TASK_GROUP_SIZE) { uint m = p.meshletOffset + i; @@ -57,53 +118,8 @@ void taskMain( // if the meshlet is outside of the frustum, we skip it since we cant do depth culling anyways if(meshlet.bounding.insideFrustum(viewFrustum)) { - uint2 mipDimensions = uint2((uint(pViewParams.screenDimensions.x) + BLOCK_SIZE - 1) / BLOCK_SIZE, (uint(pViewParams.screenDimensions.y) + BLOCK_SIZE - 1) / BLOCK_SIZE); - // now we calculate what mip level we need to only sample up to 4 texels covering the entire meshlet - uint2 screenCornerMin = mipDimensions; - uint2 screenCornerMax = uint2(0, 0); - // we use reverse depth, so higher values are closer - float maxDepth = 0; - { - float4 corners[8]; - corners[0] = float4(meshlet.bounding.min.x, meshlet.bounding.min.y, meshlet.bounding.min.z, 1.0f); - corners[1] = float4(meshlet.bounding.min.x, meshlet.bounding.min.y, meshlet.bounding.max.z, 1.0f); - corners[2] = float4(meshlet.bounding.min.x, meshlet.bounding.max.y, meshlet.bounding.min.z, 1.0f); - corners[3] = float4(meshlet.bounding.min.x, meshlet.bounding.max.y, meshlet.bounding.max.z, 1.0f); - corners[4] = float4(meshlet.bounding.max.x, meshlet.bounding.min.y, meshlet.bounding.min.z, 1.0f); - corners[5] = float4(meshlet.bounding.max.x, meshlet.bounding.min.y, meshlet.bounding.max.z, 1.0f); - corners[6] = float4(meshlet.bounding.max.x, meshlet.bounding.max.y, meshlet.bounding.min.z, 1.0f); - corners[7] = float4(meshlet.bounding.max.x, meshlet.bounding.max.y, meshlet.bounding.max.z, 1.0f); - for(uint i = 0; i < 8; ++i) - { - float4 clipCorner = mul(modelViewProjection, corners[i]); - float4 screenCorner = clipToScreen(clipCorner) / BLOCK_SIZE; - screenCornerMin = uint2(min(screenCornerMin.x, uint(screenCorner.x)), min(screenCornerMin.y, uint(screenCorner.y))); - screenCornerMax = uint2(max(screenCornerMax.x, uint(screenCorner.x)), max(screenCornerMax.y, uint(screenCorner.y))); - maxDepth = max(maxDepth, screenCorner.z); - } - } - uint mipOffset = 0; - // in theory this wouldnt work if no corner was in screen, as min would be greater that max, however we verified that with view culling - while(screenCornerMax.x - screenCornerMin.x > 1 || screenCornerMax.y - screenCornerMin.y > 1) - { - mipOffset += mipDimensions.x * mipDimensions.y; - mipDimensions = uint2(mipDimensions.x + 1, mipDimensions.y + 1) / 2; - screenCornerMin /= 2; - screenCornerMax /= 2; - } - - // now we sample 4 texels from the depth at the calculated mip level, this should give us the screen extent of the meshlet - float d1 = pDepthAttachment.buffer[mipOffset + (screenCornerMin.y * mipDimensions.x) + screenCornerMin.x]; - float d2 = pDepthAttachment.buffer[mipOffset + (screenCornerMin.y * mipDimensions.x) + screenCornerMax.x]; - float d3 = pDepthAttachment.buffer[mipOffset + (screenCornerMax.y * mipDimensions.x) + screenCornerMin.x]; - float d4 = pDepthAttachment.buffer[mipOffset + (screenCornerMax.y * mipDimensions.x) + screenCornerMax.x]; - - // we want to check if the minimum depth (the value farthest away) is smaller than the maximum bounding box depth - // otherwise, there is no way for the meshlet to be visible - float d = min(min(d1, d2), min(d3, d4)); - - // this is technically not correct, as the mipmap is generated with a linear filter, but we actually would need a min filter, but whatever - if(d < maxDepth) + // if the meshlet bounding box is behind the cached depth buffer, we skip + if(isBoxVisible(meshlet.bounding)) { uint index; InterlockedAdd(head, 1, index); diff --git a/res/shaders/DepthMipGen.slang b/res/shaders/DepthMipGen.slang index e1a3241..7e4ad2b 100644 --- a/res/shaders/DepthMipGen.slang +++ b/res/shaders/DepthMipGen.slang @@ -14,7 +14,7 @@ struct MipParam uint2 srcMipDim; uint2 dstMipDim; } -layout(push_constants) +layout(push_constant) ConstantBuffer pMipParam; float getSrcDepth(uint2 pos) diff --git a/src/Editor/Asset/MaterialLoader.cpp b/src/Editor/Asset/MaterialLoader.cpp index 323e808..f6b0055 100644 --- a/src/Editor/Asset/MaterialLoader.cpp +++ b/src/Editor/Asset/MaterialLoader.cpp @@ -202,7 +202,7 @@ void MaterialLoader::import(MaterialImportArgs args, PMaterialAsset asset) { } } } - asset->material = new Material(graphics, numTextures, numSamplers, numFloats, materialName, std::move(expressions), + asset->material = new Material(graphics, numTextures, numSamplers, numFloats, false, 1, materialName, std::move(expressions), std::move(parameters), std::move(mat)); asset->material->compile(); diff --git a/src/Editor/Asset/MeshLoader.cpp b/src/Editor/Asset/MeshLoader.cpp index 09eb525..87cc823 100644 --- a/src/Editor/Asset/MeshLoader.cpp +++ b/src/Editor/Asset/MeshLoader.cpp @@ -19,7 +19,6 @@ #include #include - using namespace Seele; MeshLoader::MeshLoader(Gfx::PGraphics graphics) : graphics(graphics) {} @@ -372,10 +371,13 @@ void MeshLoader::loadMaterials(const aiScene* scene, const Array& } break; }; - + bool twoSided = true; + material->Get(AI_MATKEY_TWOSIDED, twoSided); + float opacity = 1.0f; + material->Get(AI_MATKEY_OPACITY, opacity); OMaterialAsset baseMat = new MaterialAsset(importPath, materialName); - baseMat->material = new Material(graphics, numTextures, numSamplers, numFloats, materialName, std::move(expressions), - std::move(parameters), std::move(brdf)); + baseMat->material = new Material(graphics, numTextures, numSamplers, numFloats, twoSided, opacity, materialName, + std::move(expressions), std::move(parameters), std::move(brdf)); baseMat->material->compile(); graphics->getShaderCompiler()->registerMaterial(baseMat->material); globalMaterials[m] = baseMat->instantiate(InstantiationParameter{ @@ -473,8 +475,6 @@ void MeshLoader::loadGlobalMeshes(const aiScene* scene, const Arraymeshlets = std::move(meshlets); globalMeshes[meshIndex]->indices = std::move(indices); globalMeshes[meshIndex]->vertexCount = mesh->mNumVertices; - globalMeshes[meshIndex]->blas = - graphics->createBottomLevelAccelerationStructure(Gfx::BottomLevelASCreateInfo(globalMeshes[meshIndex])); } } diff --git a/src/Editor/main.cpp b/src/Editor/main.cpp index c52cfc9..215c1ad 100644 --- a/src/Editor/main.cpp +++ b/src/Editor/main.cpp @@ -63,16 +63,13 @@ int main() { .filePath = sourcePath / "import/textures/skyboxsun5deg_tn.jpg", .type = TextureImportType::TEXTURE_CUBEMAP, }); - // AssetImporter::importMesh(MeshImportArgs{ - // .filePath = sourcePath / "import/models/greek-temple/source/greek-temple.fbx", - // .importPath = "temple" - // }); AssetImporter::importMesh(MeshImportArgs{.filePath = sourcePath / "import/models/after-the-rain-vr-sound/source/Whitechapel.fbx", .importPath = "Whitechapel"}); - // AssetImporter::importMesh(MeshImportArgs{ - // .filePath = sourcePath / "import/models/nitra-castle-rawscan/source/Nitriansky.obj", - // .importPath = "Nitriansky" - // }); + //AssetImporter::importMesh(MeshImportArgs{ + // .filePath = sourcePath / "import/models/city-suburbs/city-suburbs.gltf", + // .importPath = "suburbs", + //}); + vd->commitMeshes(); WindowCreateInfo mainWindowInfo; mainWindowInfo.title = "SeeleEngine"; mainWindowInfo.width = 1920; diff --git a/src/Engine/Component/Camera.cpp b/src/Engine/Component/Camera.cpp index c6e7493..4744de9 100644 --- a/src/Engine/Component/Camera.cpp +++ b/src/Engine/Component/Camera.cpp @@ -43,5 +43,6 @@ void Camera::buildViewMatrix() { Vector lookAt = eyePos + getTransform().getForward(); viewMatrix = glm::lookAt(eyePos, lookAt, Vector(0, 1, 0)); cameraPos = eyePos; + cameraForward = getTransform().getForward(); bNeedsViewBuild = false; } diff --git a/src/Engine/Component/Camera.h b/src/Engine/Component/Camera.h index cf169e6..80789bb 100644 --- a/src/Engine/Component/Camera.h +++ b/src/Engine/Component/Camera.h @@ -16,6 +16,7 @@ struct Camera { return viewMatrix; } Vector getCameraPosition() const { return cameraPos; } + Vector getCameraForward() const { return cameraForward; } void mouseMove(float deltaX, float deltaY); void mouseScroll(float x); void moveX(float amount); @@ -29,6 +30,7 @@ struct Camera { float pitch; Matrix4 viewMatrix; Vector cameraPos; + Vector cameraForward; bool bNeedsViewBuild; }; } // namespace Component diff --git a/src/Engine/Graphics/Descriptor.h b/src/Engine/Graphics/Descriptor.h index 0df592e..310330c 100644 --- a/src/Engine/Graphics/Descriptor.h +++ b/src/Engine/Graphics/Descriptor.h @@ -54,6 +54,7 @@ DEFINE_REF(DescriptorPool) DECLARE_REF(UniformBuffer) DECLARE_REF(ShaderBuffer) DECLARE_REF(Texture) +DECLARE_REF(Texture2D) DECLARE_REF(Sampler) class DescriptorSet { public: @@ -67,7 +68,8 @@ class DescriptorSet { virtual void updateSampler(uint32_t binding, uint32 dstArrayIndex, Gfx::PSampler samplerState) = 0; virtual void updateTexture(uint32 binding, PTexture texture, PSampler samplerState = nullptr) = 0; virtual void updateTexture(uint32 binding, uint32 dstArrayIndex, PTexture texture) = 0; - virtual void updateTextureArray(uint32_t binding, Array texture) = 0; + virtual void updateTextureArray(uint32_t binding, Array texture) = 0; + virtual void updateSamplerArray(uint32_t binding, Array samplers) = 0; bool operator<(PDescriptorSet other); constexpr PDescriptorLayout getLayout() const { return layout; } diff --git a/src/Engine/Graphics/Initializer.h b/src/Engine/Graphics/Initializer.h index 48750c3..73fb534 100644 --- a/src/Engine/Graphics/Initializer.h +++ b/src/Engine/Graphics/Initializer.h @@ -98,6 +98,7 @@ struct ShaderBufferCreateInfo { DataSource sourceData = DataSource(); uint64 numElements = 1; uint32 clearValue = 0; + uint8 createCleared = 0; uint8 dynamic = 0; uint8 vertexBuffer = 0; std::string name = "Unnamed"; @@ -169,10 +170,10 @@ struct ColorBlendState { struct BlendAttachment { uint32 blendEnable = 0; SeBlendFactor srcColorBlendFactor = Gfx::SE_BLEND_FACTOR_SRC_ALPHA; - SeBlendFactor dstColorBlendFactor = Gfx::SE_BLEND_FACTOR_SRC_ALPHA; + SeBlendFactor dstColorBlendFactor = Gfx::SE_BLEND_FACTOR_DST_ALPHA; SeBlendOp colorBlendOp = Gfx::SE_BLEND_OP_ADD; - SeBlendFactor srcAlphaBlendFactor = Gfx::SE_BLEND_FACTOR_SRC_ALPHA; - SeBlendFactor dstAlphaBlendFactor = Gfx::SE_BLEND_FACTOR_SRC_ALPHA; + SeBlendFactor srcAlphaBlendFactor = Gfx::SE_BLEND_FACTOR_ONE; + SeBlendFactor dstAlphaBlendFactor = Gfx::SE_BLEND_FACTOR_ONE; SeBlendOp alphaBlendOp = Gfx::SE_BLEND_OP_ADD; SeColorComponentFlags colorWriteMask = Gfx::SE_COLOR_COMPONENT_R_BIT | Gfx::SE_COLOR_COMPONENT_G_BIT | Gfx::SE_COLOR_COMPONENT_B_BIT | Gfx::SE_COLOR_COMPONENT_A_BIT; diff --git a/src/Engine/Graphics/RenderPass/BasePass.cpp b/src/Engine/Graphics/RenderPass/BasePass.cpp index a2d7df7..9d2329f 100644 --- a/src/Engine/Graphics/RenderPass/BasePass.cpp +++ b/src/Engine/Graphics/RenderPass/BasePass.cpp @@ -75,6 +75,9 @@ BasePass::~BasePass() {} void BasePass::beginFrame(const Component::Camera& cam) { RenderPass::beginFrame(cam); + cameraPos = cam.getCameraPosition(); + cameraForward = cam.getCameraForward(); + lightCullingLayout->reset(); opaqueCulling = lightCullingLayout->allocateDescriptorSet(); transparentCulling = lightCullingLayout->allocateDescriptorSet(); @@ -95,7 +98,9 @@ void BasePass::render() { Gfx::ShaderPermutation permutation = graphics->getShaderCompiler()->getTemplate("BasePass"); permutation.setDepthCulling(true); // always use the culling info permutation.setPositionOnly(false); + Array transparentData; for (VertexData* vertexData : VertexData::getList()) { + transparentData.addAll(vertexData->getTransparentData()); vertexData->getInstanceDataSet()->updateBuffer(6, cullingBuffer); vertexData->getInstanceDataSet()->writeChanges(); permutation.setVertexData(vertexData->getTypeName()); @@ -120,6 +125,9 @@ void BasePass::render() { const Gfx::ShaderCollection* collection = graphics->getShaderCompiler()->findShaders(id); assert(collection != nullptr); + + bool twoSided = materialData.material->isTwoSided(); + if (graphics->supportMeshShading()) { Gfx::MeshPipelineCreateInfo pipelineInfo = { .taskShader = collection->taskShader, @@ -131,6 +139,10 @@ void BasePass::render() { { .samples = viewport->getSamples(), }, + .rasterizationState = + { + .cullMode = Gfx::SeCullModeFlags(twoSided ? Gfx::SE_CULL_MODE_NONE : Gfx::SE_CULL_MODE_BACK_BIT), + }, .depthStencilState = { .depthCompareOp = Gfx::SE_COMPARE_OP_GREATER_OR_EQUAL, @@ -152,6 +164,10 @@ void BasePass::render() { { .samples = viewport->getSamples(), }, + .rasterizationState = + { + .cullMode = Gfx::SeCullModeFlags(twoSided ? Gfx::SE_CULL_MODE_NONE : Gfx::SE_CULL_MODE_BACK_BIT), + }, .depthStencilState = { .depthCompareOp = Gfx::SE_COMPARE_OP_GREATER_OR_EQUAL, @@ -184,8 +200,106 @@ void BasePass::render() { commands.add(std::move(command)); } } - graphics->executeCommands(std::move(commands)); + + Map sortedDraws; + for (const auto& t : transparentData) { + Vector toCenter = Vector(t.worldPosition) - cameraPos; + float dist = glm::length(toCenter) * glm::dot(glm::normalize(toCenter), cameraForward); + sortedDraws[dist] = t; + } + Gfx::ORenderCommand command = graphics->createRenderCommand("TransparentDraw"); + command->setViewport(viewport); + for (const auto& [_, t] : sortedDraws) { + permutation.setVertexData(t.vertexData->getTypeName()); + permutation.setMaterial(t.matInst->getBaseMaterial()->getName()); + Gfx::PermutationId id(permutation); + + const Gfx::ShaderCollection* collection = graphics->getShaderCompiler()->findShaders(id); + assert(collection != nullptr); + + bool twoSided = t.matInst->getBaseMaterial()->isTwoSided(); + + if (graphics->supportMeshShading()) { + Gfx::MeshPipelineCreateInfo pipelineInfo = { + .taskShader = collection->taskShader, + .meshShader = collection->meshShader, + .fragmentShader = collection->fragmentShader, + .renderPass = renderPass, + .pipelineLayout = collection->pipelineLayout, + .multisampleState = + { + .samples = viewport->getSamples(), + }, + .rasterizationState = + { + .cullMode = Gfx::SeCullModeFlags(twoSided ? Gfx::SE_CULL_MODE_NONE : Gfx::SE_CULL_MODE_BACK_BIT), + }, + .depthStencilState = + { + .depthCompareOp = Gfx::SE_COMPARE_OP_GREATER_OR_EQUAL, + }, + .colorBlend = + { + .attachmentCount = 1, + .blendAttachments = + { + Gfx::ColorBlendState::BlendAttachment{ + .blendEnable = true, + }, + }, + }, + }; + Gfx::PGraphicsPipeline pipeline = graphics->createGraphicsPipeline(std::move(pipelineInfo)); + command->bindPipeline(pipeline); + } else { + Gfx::LegacyPipelineCreateInfo pipelineInfo = { + .vertexShader = collection->vertexShader, + .fragmentShader = collection->fragmentShader, + .renderPass = renderPass, + .pipelineLayout = collection->pipelineLayout, + .multisampleState = + { + .samples = viewport->getSamples(), + }, + .rasterizationState = + { + .cullMode = Gfx::SeCullModeFlags(twoSided ? Gfx::SE_CULL_MODE_NONE : Gfx::SE_CULL_MODE_BACK_BIT), + }, + .depthStencilState = + { + .depthCompareOp = Gfx::SE_COMPARE_OP_GREATER_OR_EQUAL, + }, + .colorBlend = + { + .attachmentCount = 1, + .blendAttachments = + { + Gfx::ColorBlendState::BlendAttachment{ + .blendEnable = true, + }, + }, + }, + }; + Gfx::PGraphicsPipeline pipeline = graphics->createGraphicsPipeline(std::move(pipelineInfo)); + command->bindPipeline(pipeline); + } + command->bindDescriptor({viewParamsSet, t.vertexData->getVertexDataSet(), t.vertexData->getInstanceDataSet(), + scene->getLightEnvironment()->getDescriptorSet(), Material::getDescriptorSet(), transparentCulling}); + command->pushConstants(Gfx::SE_SHADER_STAGE_TASK_BIT_EXT | Gfx::SE_SHADER_STAGE_VERTEX_BIT | Gfx::SE_SHADER_STAGE_FRAGMENT_BIT, 0, + sizeof(VertexData::DrawCallOffsets), &t.offsets); + if (graphics->supportMeshShading()) { + command->drawMesh(1, 1, 1); + } else { + // command->bindIndexBuffer(t.vertexData->getIndexBuffer()); + // for (const auto& meshData : drawCall.instanceMeshData) { + // // all meshlets of a mesh share the same indices offset + // command->drawIndexed(meshData.numIndices, 1, meshData.firstIndex, vertexData->getIndicesOffset(meshData.meshletOffset), + // 0); + // } + } + } + graphics->endRenderPass(); query->endQuery(); } diff --git a/src/Engine/Graphics/RenderPass/BasePass.h b/src/Engine/Graphics/RenderPass/BasePass.h index 69ca591..990b963 100644 --- a/src/Engine/Graphics/RenderPass/BasePass.h +++ b/src/Engine/Graphics/RenderPass/BasePass.h @@ -31,6 +31,10 @@ class BasePass : public RenderPass { // use a different texture here so we can do multisampling Gfx::OTexture2D basePassDepth; + // used for transparency sorting + Vector cameraPos; + Vector cameraForward; + PCameraActor source; Gfx::OPipelineLayout basePassLayout; Gfx::ODescriptorLayout lightCullingLayout; diff --git a/src/Engine/Graphics/RenderPass/DepthCullingPass.cpp b/src/Engine/Graphics/RenderPass/DepthCullingPass.cpp index 29713a4..e864039 100644 --- a/src/Engine/Graphics/RenderPass/DepthCullingPass.cpp +++ b/src/Engine/Graphics/RenderPass/DepthCullingPass.cpp @@ -105,7 +105,9 @@ void DepthCullingPass::render() { UVector2 threadGroups = (((mipDims[i] + UVector2(1, 1)) / 2u) + UVector2(BLOCK_SIZE - 1, BLOCK_SIZE - 1)) / uint32(BLOCK_SIZE); computeCommand->dispatch(threadGroups.x, threadGroups.y, 1); } - + Array computeCommands; + computeCommands.add(std::move(computeCommand)); + graphics->executeCommands(std::move(computeCommands)); depthMipBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_TASK_SHADER_BIT_EXT); diff --git a/src/Engine/Graphics/RenderPass/TextPass.cpp b/src/Engine/Graphics/RenderPass/TextPass.cpp index 867f8c6..d68831c 100644 --- a/src/Engine/Graphics/RenderPass/TextPass.cpp +++ b/src/Engine/Graphics/RenderPass/TextPass.cpp @@ -186,7 +186,7 @@ TextPass::FontData& TextPass::getFontData(PFontAsset font) { const auto& fontGlyphs = font->getGlyphData(); FontData& fd = fontData[font]; Array glyphData; - Array textures; + Array textures; glyphData.reserve(fontGlyphs.size()); for (const auto& [key, value] : fontGlyphs) { fd.characterToGlyphIndex[key] = static_cast(glyphData.size()); diff --git a/src/Engine/Graphics/RenderPass/UIPass.h b/src/Engine/Graphics/RenderPass/UIPass.h index e716033..055ee06 100644 --- a/src/Engine/Graphics/RenderPass/UIPass.h +++ b/src/Engine/Graphics/RenderPass/UIPass.h @@ -37,7 +37,7 @@ class UIPass : public RenderPass { Gfx::OPipelineLayout pipelineLayout; Array renderElements; - Array usedTextures; + Array usedTextures; }; DEFINE_REF(UIPass); } // namespace Seele diff --git a/src/Engine/Graphics/RenderPass/VisibilityPass.cpp b/src/Engine/Graphics/RenderPass/VisibilityPass.cpp index 7ea3f38..c3e332f 100644 --- a/src/Engine/Graphics/RenderPass/VisibilityPass.cpp +++ b/src/Engine/Graphics/RenderPass/VisibilityPass.cpp @@ -79,6 +79,7 @@ void VisibilityPass::publishOutputs() { cullingBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ .clearValue = 0xffffffff, + .createCleared = true, .dynamic = true, .name = "CullingBuffer", }); diff --git a/src/Engine/Graphics/VertexData.cpp b/src/Engine/Graphics/VertexData.cpp index 3444871..edc201a 100644 --- a/src/Engine/Graphics/VertexData.cpp +++ b/src/Engine/Graphics/VertexData.cpp @@ -17,6 +17,8 @@ uint64 VertexData::meshletCount = 0; void VertexData::resetMeshData() { std::unique_lock l(materialDataLock); + transparentInstanceData.clear(); + transparentMeshData.clear(); for (auto& mat : materialData) { for (auto& inst : mat.instances) { inst.instanceData.clear(); @@ -36,6 +38,32 @@ void VertexData::updateMesh(entt::entity id, uint32 meshIndex, PMesh mesh, Compo std::unique_lock l(materialDataLock); PMaterialInstance referencedInstance = mesh->referencedMaterial->getHandle(); PMaterial mat = referencedInstance->getBaseMaterial(); + const auto& data = meshData[mesh->id]; + + Matrix4 transformMatrix = transform.toMatrix() * mesh->transform; + InstanceData inst = InstanceData{ + .transformMatrix = transformMatrix, + .inverseTransformMatrix = glm::inverse(transformMatrix), + }; + + if (mat->hasTransparency()) { + auto params = referencedInstance->getMaterialOffsets(); + transparentData.add(TransparentDraw{ + .matInst = referencedInstance, + .vertexData = this, + .offsets = + { + .instanceOffset = static_cast(transparentInstanceData.size()), + .textureOffset = params.textureOffset, + .samplerOffset = params.samplerOffset, + .floatOffset = params.floatOffset, + }, + .worldPosition = Vector(inst.transformMatrix[3]), + }); + transparentInstanceData.add(inst); + transparentMeshData.add(data); + return; + } if (materialData.size() <= mat->getId()) { materialData.resize(mat->getId() + 1); } @@ -47,12 +75,7 @@ void VertexData::updateMesh(entt::entity id, uint32 meshIndex, PMesh mesh, Compo BatchedDrawCall& matInstanceData = matData.instances[referencedInstance->getId()]; matInstanceData.materialInstance = referencedInstance; - Matrix4 transformMatrix = transform.toMatrix() * mesh->transform; - matInstanceData.instanceData.add(InstanceData{ - .transformMatrix = transformMatrix, - .inverseTransformMatrix = glm::inverse(transformMatrix), - }); - const auto& data = meshData[mesh->id]; + matInstanceData.instanceData.add(inst); auto [instanceId, meshletOffset] = getCullingMapping(id, meshIndex, data.numMeshlets); matInstanceData.instanceMeshData.add(data); matInstanceData.cullingOffsets.add(meshletOffset); @@ -157,6 +180,25 @@ void VertexData::createDescriptors() { }); instanceMeshDataBuffer->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, Gfx::SE_ACCESS_MEMORY_READ_BIT, Gfx::SE_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + + transparentInstanceDataBuffer->rotateBuffer(sizeof(InstanceData) * transparentInstanceData.size()); + transparentInstanceDataBuffer->updateContents(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(InstanceData) * transparentInstanceData.size(), + .data = (uint8*)transparentInstanceData.data(), + }, + }); + + transparentMeshDataBuffer->rotateBuffer(sizeof(MeshData) * transparentMeshData.size()); + transparentMeshDataBuffer->updateContents(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(MeshData) * transparentMeshData.size(), + .data = (uint8*)transparentMeshData.data(), + }, + }); + instanceDataLayout->reset(); descriptorSet = instanceDataLayout->allocateDescriptorSet(); descriptorSet->updateBuffer(0, instanceBuffer); @@ -169,176 +211,197 @@ void VertexData::createDescriptors() { } void VertexData::loadMesh(MeshId id, Array loadedIndices, Array loadedMeshlets) { - assert(loadedMeshlets.size() < 2048); - std::unique_lock l(vertexDataLock); - meshlets.reserve(meshlets.size() + loadedMeshlets.size()); - vertexIndices.reserve(vertexIndices.size() + loadedMeshlets.size() * Gfx::numVerticesPerMeshlet); - primitiveIndices.reserve(primitiveIndices.size() + loadedMeshlets.size() * Gfx::numPrimitivesPerMeshlet * 3); - uint32 meshletOffset = meshlets.size(); - AABB meshAABB; - for (uint32 i = 0; i < loadedMeshlets.size(); ++i) { - Meshlet& m = loadedMeshlets[i]; - meshAABB = meshAABB.combine(m.boundingBox); - uint32 vertexOffset = vertexIndices.size(); - vertexIndices.resize(vertexOffset + m.numVertices); - std::memcpy(vertexIndices.data() + vertexOffset, m.uniqueVertices, m.numVertices * sizeof(uint32)); - uint32 primitiveOffset = primitiveIndices.size(); - primitiveIndices.resize(primitiveOffset + (m.numPrimitives * 3)); - std::memcpy(primitiveIndices.data() + primitiveOffset, m.primitiveLayout, m.numPrimitives * 3 * sizeof(uint8)); - meshlets.add(MeshletDescription{ - .bounding = m.boundingBox, //.toSphere(), - .vertexCount = m.numVertices, - .primitiveCount = m.numPrimitives, - .vertexOffset = vertexOffset, - .primitiveOffset = primitiveOffset, - .color = Vector((float)rand() / RAND_MAX, (float)rand() / RAND_MAX, (float)rand() / RAND_MAX), - .indicesOffset = (uint32)meshOffsets[id], + std::unique_lock l(vertexDataLock); + meshlets.reserve(meshlets.size() + loadedMeshlets.size()); + vertexIndices.reserve(vertexIndices.size() + loadedMeshlets.size() * Gfx::numVerticesPerMeshlet); + primitiveIndices.reserve(primitiveIndices.size() + loadedMeshlets.size() * Gfx::numPrimitivesPerMeshlet * 3); + uint32 meshletOffset = meshlets.size(); + AABB meshAABB; + for (uint32 i = 0; i < loadedMeshlets.size(); ++i) { + Meshlet& m = loadedMeshlets[i]; + meshAABB = meshAABB.combine(m.boundingBox); + uint32 vertexOffset = vertexIndices.size(); + vertexIndices.resize(vertexOffset + m.numVertices); + std::memcpy(vertexIndices.data() + vertexOffset, m.uniqueVertices, m.numVertices * sizeof(uint32)); + uint32 primitiveOffset = primitiveIndices.size(); + primitiveIndices.resize(primitiveOffset + (m.numPrimitives * 3)); + std::memcpy(primitiveIndices.data() + primitiveOffset, m.primitiveLayout, m.numPrimitives * 3 * sizeof(uint8)); + meshlets.add(MeshletDescription{ + .bounding = m.boundingBox, //.toSphere(), + .vertexCount = m.numVertices, + .primitiveCount = m.numPrimitives, + .vertexOffset = vertexOffset, + .primitiveOffset = primitiveOffset, + .color = Vector((float)rand() / RAND_MAX, (float)rand() / RAND_MAX, (float)rand() / RAND_MAX), + .indicesOffset = (uint32)meshOffsets[id], + }); + } + meshData[id] = MeshData{ + .bounding = meshAABB, //.toSphere(), + .numMeshlets = (uint32)loadedMeshlets.size(), + .meshletOffset = meshletOffset, + .firstIndex = (uint32)indices.size(), + .numIndices = (uint32)loadedIndices.size(), + }; + + indices.resize(indices.size() + loadedIndices.size()); + std::memcpy(indices.data() + meshData[id].firstIndex, loadedIndices.data(), loadedIndices.size() * sizeof(uint32)); +} + +void VertexData::commitMeshes() { + indexBuffer = graphics->createIndexBuffer(IndexBufferCreateInfo{ + .sourceData = + { + .size = sizeof(uint32) * indices.size(), + .data = (uint8*)indices.data(), + }, + .indexType = Gfx::SE_INDEX_TYPE_UINT32, + .name = "IndexBuffer", + }); + meshletBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(MeshletDescription) * meshlets.size(), + .data = (uint8*)meshlets.data(), + }, + .numElements = meshlets.size(), + .dynamic = false, + .name = "MeshletBuffer", + }); + vertexIndicesBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(uint32) * vertexIndices.size(), + .data = (uint8*)vertexIndices.data(), + }, + .numElements = vertexIndices.size(), + .dynamic = false, + .name = "VertexIndicesBuffer", }); - } - meshData[id] = MeshData{ - .bounding = meshAABB, //.toSphere(), - .numMeshlets = (uint32)loadedMeshlets.size(), - .meshletOffset = meshletOffset, - .firstIndex = (uint32)indices.size(), - .numIndices = (uint32)loadedIndices.size(), - }; - indices.resize(indices.size() + loadedIndices.size()); - std::memcpy(indices.data() + meshData[id].firstIndex, loadedIndices.data(), loadedIndices.size() * sizeof(uint32)); - indexBuffer = graphics->createIndexBuffer(IndexBufferCreateInfo{ - .sourceData = - { - .size = sizeof(uint32) * indices.size(), - .data = (uint8*)indices.data(), - }, - .indexType = Gfx::SE_INDEX_TYPE_UINT32, - .name = "IndexBuffer", - }); - meshletBuffer = graphics->createShaderBuffer( - ShaderBufferCreateInfo{.sourceData = {.size = sizeof(MeshletDescription) * meshlets.size(), .data = (uint8*)meshlets.data()}, - .numElements = meshlets.size(), - .dynamic = false, - .name = "MeshletBuffer"}); - - vertexIndicesBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{.sourceData = - { - .size = sizeof(uint32) * vertexIndices.size(), - .data = (uint8*)vertexIndices.data(), - }, - .numElements = vertexIndices.size(), - .dynamic = false, - .name = "VertexIndicesBuffer"}); - - primitiveIndicesBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ - .sourceData = - { - .size = sizeof(uint8) * primitiveIndices.size(), - .data = (uint8*)primitiveIndices.data(), - }, - .numElements = primitiveIndices.size(), - .dynamic = false, - .name = "PrimitiveIndicesBuffer", - }); + primitiveIndicesBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(uint8) * primitiveIndices.size(), + .data = (uint8*)primitiveIndices.data(), + }, + .numElements = primitiveIndices.size(), + .dynamic = false, + .name = "PrimitiveIndicesBuffer", + }); } MeshId VertexData::allocateVertexData(uint64 numVertices) { - std::unique_lock l(vertexDataLock); - MeshId res{idCounter++}; - meshOffsets[res] = head; - meshVertexCounts[res] = numVertices; - head += numVertices; - if (head > verticesAllocated) { - verticesAllocated = std::max(head, verticesAllocated + NUM_DEFAULT_ELEMENTS); - resizeBuffers(); - } - return res; + std::unique_lock l(vertexDataLock); + MeshId res{idCounter++}; + meshOffsets[res] = head; + meshVertexCounts[res] = numVertices; + head += numVertices; + if (head > verticesAllocated) { + verticesAllocated = std::max(head, verticesAllocated + NUM_DEFAULT_ELEMENTS); + resizeBuffers(); + } + return res; } -uint64 VertexData::getMeshOffset(MeshId id) { return meshOffsets[id]; } +uint64 VertexData::getMeshOffset(MeshId id) { + return meshOffsets[id]; } -uint64 VertexData::getMeshVertexCount(MeshId id) { return meshVertexCounts[id]; } +uint64 VertexData::getMeshVertexCount(MeshId id) { + return meshVertexCounts[id]; } List vertexDataList; -List VertexData::getList() { return vertexDataList; } +List VertexData::getList() { + return vertexDataList; } VertexData* VertexData::findByTypeName(std::string name) { - for (auto vd : vertexDataList) { - if (vd->getTypeName() == name) { - return vd; + for (auto vd : vertexDataList) { + if (vd->getTypeName() == name) { + return vd; + } } - } - return nullptr; + return nullptr; } void VertexData::init(Gfx::PGraphics _graphics) { - graphics = _graphics; - verticesAllocated = NUM_DEFAULT_ELEMENTS; - instanceDataLayout = graphics->createDescriptorLayout("pScene"); + graphics = _graphics; + verticesAllocated = NUM_DEFAULT_ELEMENTS; + instanceDataLayout = graphics->createDescriptorLayout("pScene"); - // instanceData - instanceDataLayout->addDescriptorBinding(Gfx::DescriptorBinding{ - .binding = 0, - .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, - }); - // meshData - instanceDataLayout->addDescriptorBinding(Gfx::DescriptorBinding{ - .binding = 1, - .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, - }); - // meshletData - instanceDataLayout->addDescriptorBinding( - Gfx::DescriptorBinding{.binding = 2, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); - // primitiveIndices - instanceDataLayout->addDescriptorBinding( - Gfx::DescriptorBinding{.binding = 3, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); - // vertexIndices - instanceDataLayout->addDescriptorBinding( - Gfx::DescriptorBinding{.binding = 4, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); - // cullingOffset - instanceDataLayout->addDescriptorBinding( - Gfx::DescriptorBinding{.binding = 5, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); - // cullingInfos - instanceDataLayout->addDescriptorBinding( - Gfx::DescriptorBinding{.binding = 6, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + // instanceData + instanceDataLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .binding = 0, + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + // meshData + instanceDataLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .binding = 1, + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + // meshletData + instanceDataLayout->addDescriptorBinding( + Gfx::DescriptorBinding{.binding = 2, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + // primitiveIndices + instanceDataLayout->addDescriptorBinding( + Gfx::DescriptorBinding{.binding = 3, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + // vertexIndices + instanceDataLayout->addDescriptorBinding( + Gfx::DescriptorBinding{.binding = 4, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + // cullingOffset + instanceDataLayout->addDescriptorBinding( + Gfx::DescriptorBinding{.binding = 5, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + // cullingInfos + instanceDataLayout->addDescriptorBinding( + Gfx::DescriptorBinding{.binding = 6, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); - instanceDataLayout->create(); + instanceDataLayout->create(); - cullingOffsetBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ - .dynamic = true, - .name = "MeshletOffset", - }); - instanceBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ - .dynamic = true, - .name = "InstanceBuffer", - }); - instanceMeshDataBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ - .dynamic = true, - .name = "MeshDataBuffer", - }); - resizeBuffers(); - graphics->getShaderCompiler()->registerVertexData(this); + cullingOffsetBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .dynamic = true, + .name = "MeshletOffset", + }); + instanceBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .dynamic = true, + .name = "InstanceBuffer", + }); + instanceMeshDataBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .dynamic = true, + .name = "MeshDataBuffer", + }); + + transparentInstanceDataBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .dynamic = true, + .name = "TransparentInstanceBuffer", + }); + transparentMeshDataBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .dynamic = true, + .name = "TransparentMeshBuffer", + }); + + resizeBuffers(); + graphics->getShaderCompiler()->registerVertexData(this); } void VertexData::destroy() { - instanceBuffer = nullptr; - instanceMeshDataBuffer = nullptr; - instanceDataLayout = nullptr; - meshletBuffer = nullptr; - vertexIndicesBuffer = nullptr; - primitiveIndicesBuffer = nullptr; - indexBuffer = nullptr; - meshData.clear(); - materialData.clear(); + instanceBuffer = nullptr; + instanceMeshDataBuffer = nullptr; + instanceDataLayout = nullptr; + meshletBuffer = nullptr; + vertexIndicesBuffer = nullptr; + primitiveIndicesBuffer = nullptr; + indexBuffer = nullptr; + meshData.clear(); + materialData.clear(); } VertexData::CullingMapping VertexData::getCullingMapping(entt::entity id, uint32 meshIndex, uint32 numMeshlets) { - MeshMapping key = MeshMapping{.id = id, .meshId = meshIndex}; - if (!instanceIdMap.contains(key)) { - instanceIdMap[key] = CullingMapping{.instanceId = instanceCount++, .cullingOffset = uint32(meshletCount)}; - meshletCount += numMeshlets; - } - return instanceIdMap[key]; + MeshMapping key = MeshMapping{.id = id, .meshId = meshIndex}; + if (!instanceIdMap.contains(key)) { + instanceIdMap[key] = CullingMapping{.instanceId = instanceCount++, .cullingOffset = uint32(meshletCount)}; + meshletCount += numMeshlets; + } + return instanceIdMap[key]; } VertexData::VertexData() : idCounter(0), head(0), verticesAllocated(0), dirty(false) {} diff --git a/src/Engine/Graphics/VertexData.h b/src/Engine/Graphics/VertexData.h index 20c2310..296dbcd 100644 --- a/src/Engine/Graphics/VertexData.h +++ b/src/Engine/Graphics/VertexData.h @@ -41,10 +41,17 @@ class VertexData { PMaterial material; Array instances; }; + struct TransparentDraw { + PMaterialInstance matInst; + VertexData* vertexData; + DrawCallOffsets offsets; + Vector worldPosition; + }; void resetMeshData(); void updateMesh(entt::entity id, uint32 meshIndex, PMesh mesh, Component::Transform& transform); void createDescriptors(); void loadMesh(MeshId id, Array indices, Array meshlets); + void commitMeshes(); MeshId allocateVertexData(uint64 numVertices); uint64 getMeshOffset(MeshId id); uint64 getMeshVertexCount(MeshId id); @@ -60,6 +67,7 @@ class VertexData { Gfx::PDescriptorLayout getInstanceDataLayout() { return instanceDataLayout; } Gfx::PDescriptorSet getInstanceDataSet() { return descriptorSet; } const Array& getMaterialData() const { return materialData; } + const Array& getTransparentData() const { return transparentData; } const MeshData& getMeshData(MeshId id) { return meshData[id]; } uint64 getIndicesOffset(uint32 meshletIndex) { return meshlets[meshletIndex].indicesOffset; } uint64 getNumInstances() const { return instanceData.size(); } @@ -91,10 +99,13 @@ class VertexData { }; std::mutex materialDataLock; Array materialData; + Array transparentData; + std::mutex vertexDataLock; Map meshData; Map meshOffsets; Map meshVertexCounts; + Array meshlets; Array primitiveIndices; Array vertexIndices; @@ -121,8 +132,15 @@ class VertexData { // Material data Array instanceData; Gfx::OShaderBuffer instanceBuffer; + Array instanceMeshData; Gfx::OShaderBuffer instanceMeshDataBuffer; + + Array transparentInstanceData; + Gfx::OShaderBuffer transparentInstanceDataBuffer; + Array transparentMeshData; + Gfx::OShaderBuffer transparentMeshDataBuffer; + Gfx::PDescriptorSet descriptorSet; uint64 idCounter; uint64 head; diff --git a/src/Engine/Graphics/Vulkan/Buffer.cpp b/src/Engine/Graphics/Vulkan/Buffer.cpp index 90e185d..bb50514 100644 --- a/src/Engine/Graphics/Vulkan/Buffer.cpp +++ b/src/Engine/Graphics/Vulkan/Buffer.cpp @@ -10,8 +10,8 @@ using namespace Seele::Vulkan; BufferAllocation::BufferAllocation(PGraphics graphics, const std::string& name, VkBufferCreateInfo bufferInfo, VmaAllocationCreateInfo allocInfo, Gfx::QueueType owner, uint64 alignment) - : CommandBoundResource(graphics), size(bufferInfo.size), owner(owner) { - VK_CHECK(vmaCreateBufferWithAlignment(graphics->getAllocator(), &bufferInfo, &allocInfo, alignment, &buffer, &allocation, nullptr)); + : CommandBoundResource(graphics), size(bufferInfo.size), name(name), owner(owner) { + VK_CHECK(vmaCreateBufferWithAlignment(graphics->getAllocator(), &bufferInfo, &allocInfo, alignment, &buffer, &allocation, &info)); vmaGetAllocationMemoryProperties(graphics->getAllocator(), allocation, &properties); VkDebugUtilsObjectNameInfoEXT nameInfo = { .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, @@ -167,10 +167,10 @@ void BufferAllocation::readContents(uint64 regionOffset, uint64 regionSize, void } Buffer::Buffer(PGraphics graphics, uint64 size, VkBufferUsageFlags usage, Gfx::QueueType queueType, bool dynamic, std::string name, - uint32 clearValue) + bool createCleared, uint32 clearValue) : graphics(graphics), currentBuffer(0), initialOwner(queueType), usage(usage | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT), - dynamic(dynamic), name(name), clearValue(clearValue) { + dynamic(dynamic), createCleared(createCleared), name(name), clearValue(clearValue) { if (size > 0) { buffers.add(nullptr); createBuffer(size, 0); @@ -235,10 +235,13 @@ void Buffer::createBuffer(uint64 size, uint32 destIndex) { .usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, }; buffers[destIndex] = new BufferAllocation(graphics, name, info, allocInfo, initialOwner); - PCommand command = graphics->getQueueCommands(initialOwner)->getCommands(); - vkCmdFillBuffer(command->getHandle(), buffers[destIndex]->buffer, 0, VK_WHOLE_SIZE, clearValue); - pipelineBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + if (createCleared) + { + PCommand command = graphics->getQueueCommands(initialOwner)->getCommands(); + vkCmdFillBuffer(command->getHandle(), buffers[destIndex]->buffer, 0, VK_WHOLE_SIZE, clearValue); + pipelineBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + } } } @@ -321,7 +324,7 @@ ShaderBuffer::ShaderBuffer(PGraphics graphics, const ShaderBufferCreateInfo& cre (createInfo.vertexBuffer ? VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT : 0), - createInfo.sourceData.owner, createInfo.dynamic, createInfo.name) { + createInfo.sourceData.owner, createInfo.dynamic, createInfo.name, createInfo.createCleared, createInfo.clearValue) { if (createInfo.sourceData.size > 0 && createInfo.sourceData.data != nullptr) { getAlloc()->updateContents(createInfo.sourceData.offset, createInfo.sourceData.size, createInfo.sourceData.data); } diff --git a/src/Engine/Graphics/Vulkan/Buffer.h b/src/Engine/Graphics/Vulkan/Buffer.h index 5870d7e..de5f187 100644 --- a/src/Engine/Graphics/Vulkan/Buffer.h +++ b/src/Engine/Graphics/Vulkan/Buffer.h @@ -22,13 +22,15 @@ class BufferAllocation : public CommandBoundResource { VmaAllocationInfo info = VmaAllocationInfo(); VkMemoryPropertyFlags properties = 0; uint64 size = 0; + std::string name; VkDeviceAddress deviceAddress; Gfx::QueueType owner; }; DEFINE_REF(BufferAllocation); class Buffer { public: - Buffer(PGraphics graphics, uint64 size, VkBufferUsageFlags usage, Gfx::QueueType initialOwner, bool dynamic, std::string name, uint32 clearValue = 0); + Buffer(PGraphics graphics, uint64 size, VkBufferUsageFlags usage, Gfx::QueueType initialOwner, bool dynamic, std::string name, + bool createCleared = false, uint32 clearValue = 0); virtual ~Buffer(); VkBuffer getHandle() const { return buffers[currentBuffer]->buffer; } VkDeviceAddress getDeviceAddress() const { return buffers[currentBuffer]->deviceAddress; } @@ -44,6 +46,7 @@ class Buffer { Array buffers; VkBufferUsageFlags usage; bool dynamic; + bool createCleared; std::string name; uint32 clearValue; void rotateBuffer(uint64 size, bool preserveContents = false); diff --git a/src/Engine/Graphics/Vulkan/Command.cpp b/src/Engine/Graphics/Vulkan/Command.cpp index 0c2b640..ed70621 100644 --- a/src/Engine/Graphics/Vulkan/Command.cpp +++ b/src/Engine/Graphics/Vulkan/Command.cpp @@ -7,7 +7,6 @@ #include "RenderPass.h" #include "Window.h" - using namespace Seele; using namespace Seele::Vulkan; @@ -151,8 +150,7 @@ void Command::waitForCommand(uint32 timeout) { checkFence(); } -void Command::setPipelineStatisticsFlags(VkQueryPipelineStatisticFlags flags) -{ statisticsFlags = flags; } +void Command::setPipelineStatisticsFlags(VkQueryPipelineStatisticFlags flags) { statisticsFlags = flags; } void Command::bindResource(PCommandBoundResource resource) { resource->bind(); @@ -239,8 +237,10 @@ void RenderCommand::bindDescriptor(Gfx::PDescriptorSet descriptorSet, Arraybind(); boundResources.add(descriptor.getHandle()); for (auto binding : descriptor->boundResources) { - binding->bind(); - boundResources.add(binding); + for (auto res : binding) { + res->bind(); + boundResources.add(res); + } } VkDescriptorSet setHandle = descriptor->getHandle(); @@ -260,8 +260,13 @@ void RenderCommand::bindDescriptor(const Array& descriptorS boundResources.add(descriptorSet.getHandle()); for (auto binding : descriptorSet->boundResources) { - binding->bind(); - boundResources.add(binding); + for (auto res : binding) { + // partially bound descriptors can include nulls + if (res != nullptr) { + res->bind(); + boundResources.add(res); + } + } } sets[pipeline->getPipelineLayout()->findParameter(descriptorSet->getName())] = descriptorSet->getHandle(); } @@ -316,7 +321,7 @@ void RenderCommand::drawMeshIndirect(Gfx::PShaderBuffer buffer, uint64 offset, u vkCmdDrawMeshTasksIndirectEXT(handle, buffer.cast()->getHandle(), offset, drawCount, stride); } -void RenderCommand::traceRays() { } +void RenderCommand::traceRays() {} ComputeCommand::ComputeCommand(PGraphics graphics, VkCommandPool cmdPool) : graphics(graphics), owner(cmdPool) { VkCommandBufferAllocateInfo allocInfo = { @@ -378,8 +383,10 @@ void ComputeCommand::bindDescriptor(Gfx::PDescriptorSet descriptorSet, ArrayboundResources) { - binding->bind(); - boundResources.add(binding); + for (auto res : binding) { + res->bind(); + boundResources.add(res); + } } VkDescriptorSet setHandle = descriptor->getHandle(); @@ -398,9 +405,12 @@ void ComputeCommand::bindDescriptor(const Array& descriptor boundResources.add(descriptorSet.getHandle()); // std::cout << "Binding descriptor " << descriptorSet->getHandle() << " to cmd " << handle << std::endl; + for (auto binding : descriptorSet->boundResources) { - binding->bind(); - boundResources.add(binding); + for (auto res : binding) { + res->bind(); + boundResources.add(res); + } } sets[pipeline->getPipelineLayout()->findParameter(descriptorSet->getName())] = descriptorSet->getHandle(); } diff --git a/src/Engine/Graphics/Vulkan/Descriptor.cpp b/src/Engine/Graphics/Vulkan/Descriptor.cpp index 1102a5a..a02cd5d 100644 --- a/src/Engine/Graphics/Vulkan/Descriptor.cpp +++ b/src/Engine/Graphics/Vulkan/Descriptor.cpp @@ -168,13 +168,17 @@ DescriptorSet::DescriptorSet(PGraphics graphics, PDescriptorPool owner) : Gfx::DescriptorSet(owner->getLayout()), CommandBoundResource(graphics), setHandle(VK_NULL_HANDLE), graphics(graphics), owner(owner), bindCount(0), currentlyInUse(false) { boundResources.resize(owner->getLayout()->getBindings().size()); + for (uint32 i = 0; i < boundResources.size(); ++i) + { + boundResources[i].resize(owner->getLayout()->getBindings()[i].descriptorCount); + } } DescriptorSet::~DescriptorSet() {} void DescriptorSet::updateBuffer(uint32_t binding, Gfx::PUniformBuffer uniformBuffer) { PUniformBuffer vulkanBuffer = uniformBuffer.cast(); - if (boundResources[binding] == vulkanBuffer->getAlloc()) { + if (boundResources[binding][0] == vulkanBuffer->getAlloc()) { return; } @@ -195,12 +199,12 @@ void DescriptorSet::updateBuffer(uint32_t binding, Gfx::PUniformBuffer uniformBu .pBufferInfo = &bufferInfos.back(), }); - boundResources[binding] = vulkanBuffer->getAlloc(); + boundResources[binding][0] = vulkanBuffer->getAlloc(); } void DescriptorSet::updateBuffer(uint32_t binding, Gfx::PShaderBuffer shaderBuffer) { PShaderBuffer vulkanBuffer = shaderBuffer.cast(); - if (boundResources[binding] == vulkanBuffer->getAlloc()) { + if (boundResources[binding][0] == vulkanBuffer->getAlloc()) { return; } @@ -220,12 +224,12 @@ void DescriptorSet::updateBuffer(uint32_t binding, Gfx::PShaderBuffer shaderBuff .pBufferInfo = &bufferInfos.back(), }); - boundResources[binding] = vulkanBuffer->getAlloc(); + boundResources[binding][0] = vulkanBuffer->getAlloc(); } void DescriptorSet::updateBuffer(uint32_t binding, uint32 index, Gfx::PShaderBuffer shaderBuffer) { PShaderBuffer vulkanBuffer = shaderBuffer.cast(); - if (boundResources[binding] == vulkanBuffer->getAlloc()) { + if (boundResources[binding][index] == vulkanBuffer->getAlloc()) { return; } @@ -246,12 +250,12 @@ void DescriptorSet::updateBuffer(uint32_t binding, uint32 index, Gfx::PShaderBuf .pBufferInfo = &bufferInfos.back(), }); - boundResources[binding] = vulkanBuffer->getAlloc(); + boundResources[binding][index] = vulkanBuffer->getAlloc(); } void DescriptorSet::updateSampler(uint32_t binding, Gfx::PSampler samplerState) { PSampler vulkanSampler = samplerState.cast(); - if (boundResources[binding] == vulkanSampler->getHandle()) { + if (boundResources[binding][0] == vulkanSampler->getHandle()) { return; } @@ -272,13 +276,13 @@ void DescriptorSet::updateSampler(uint32_t binding, Gfx::PSampler samplerState) .pImageInfo = &imageInfos.back(), }); - boundResources[binding] = vulkanSampler->getHandle(); + boundResources[binding][0] = vulkanSampler->getHandle(); } void DescriptorSet::updateSampler(uint32_t binding, uint32 dstArrayIndex, Gfx::PSampler samplerState) { PSampler vulkanSampler = samplerState.cast(); - if (boundResources[binding] == vulkanSampler->getHandle()) { + if (boundResources[binding][dstArrayIndex] == vulkanSampler->getHandle()) { return; } @@ -299,13 +303,13 @@ void DescriptorSet::updateSampler(uint32_t binding, uint32 dstArrayIndex, Gfx::P .pImageInfo = &imageInfos.back(), }); - boundResources[binding] = vulkanSampler->getHandle(); + boundResources[binding][dstArrayIndex] = vulkanSampler->getHandle(); } void DescriptorSet::updateTexture(uint32_t binding, Gfx::PTexture texture, Gfx::PSampler samplerState) { TextureBase* vulkanTexture = texture.cast().getHandle(); - if (boundResources[binding] == vulkanTexture->getHandle()) { + if (boundResources[binding][0] == vulkanTexture->getHandle()) { return; } @@ -332,13 +336,13 @@ void DescriptorSet::updateTexture(uint32_t binding, Gfx::PTexture texture, Gfx:: .pImageInfo = &imageInfos.back(), }); - boundResources[binding] = vulkanTexture->getHandle(); + boundResources[binding][0] = vulkanTexture->getHandle(); } void DescriptorSet::updateTexture(uint32 binding, uint32 dstArrayIndex, Gfx::PTexture texture) { TextureBase* vulkanTexture = texture.cast().getHandle(); - if (boundResources[binding] == vulkanTexture->getHandle()) { + if (boundResources[binding][dstArrayIndex] == vulkanTexture->getHandle()) { return; } @@ -359,17 +363,16 @@ void DescriptorSet::updateTexture(uint32 binding, uint32 dstArrayIndex, Gfx::PTe .pImageInfo = &imageInfos.back(), }); - boundResources[binding] = vulkanTexture->getHandle(); + boundResources[binding][dstArrayIndex] = vulkanTexture->getHandle(); } -void DescriptorSet::updateTextureArray(uint32_t binding, Array textures) { +void DescriptorSet::updateTextureArray(uint32_t binding, Array textures) { // maybe make this a parameter? uint32 arrayElement = 0; - boundResources.resize(binding + textures.size()); for (auto& gfxTexture : textures) { TextureBase* vulkanTexture = gfxTexture.cast().getHandle(); - if (boundResources[binding + arrayElement] == vulkanTexture->getHandle()) { + if (boundResources[binding][arrayElement] == vulkanTexture->getHandle()) { continue; } imageInfos.add(VkDescriptorImageInfo{ @@ -382,7 +385,7 @@ void DescriptorSet::updateTextureArray(uint32_t binding, Array te if (vulkanTexture->getUsage() & VK_IMAGE_USAGE_STORAGE_BIT) { descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; } - boundResources[binding + arrayElement] = vulkanTexture->getHandle(); + boundResources[binding][arrayElement] = vulkanTexture->getHandle(); writeDescriptors.add(VkWriteDescriptorSet{ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .pNext = nullptr, @@ -393,10 +396,40 @@ void DescriptorSet::updateTextureArray(uint32_t binding, Array te .descriptorType = descriptorType, .pImageInfo = &imageInfos.back(), }); - vulkanTexture->getHandle()->bind(); } } + +void DescriptorSet::updateSamplerArray(uint32_t binding, Array samplers) { + // maybe make this a parameter? + uint32 arrayElement = 0; + for (auto& gfxSampler : samplers) { + PSampler vulkanSampler = gfxSampler.cast(); + if (boundResources[binding][arrayElement] == vulkanSampler->getHandle()) { + continue; + } + imageInfos.add(VkDescriptorImageInfo{ + .sampler = vulkanSampler->getHandle()->sampler, + .imageView = VK_NULL_HANDLE, + .imageLayout = VK_IMAGE_LAYOUT_UNDEFINED, + }); + + VkDescriptorType descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + boundResources[binding][arrayElement] = vulkanSampler->getHandle(); + writeDescriptors.add(VkWriteDescriptorSet{ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext = nullptr, + .dstSet = setHandle, + .dstBinding = binding, + .dstArrayElement = arrayElement++, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER, + .pImageInfo = &imageInfos.back(), + }); + } +} + + void DescriptorSet::writeChanges() { if (writeDescriptors.size() > 0) { if (isCurrentlyBound()) { diff --git a/src/Engine/Graphics/Vulkan/Descriptor.h b/src/Engine/Graphics/Vulkan/Descriptor.h index bae44d9..8a3aadd 100644 --- a/src/Engine/Graphics/Vulkan/Descriptor.h +++ b/src/Engine/Graphics/Vulkan/Descriptor.h @@ -55,7 +55,8 @@ class DescriptorSet : public Gfx::DescriptorSet, public CommandBoundResource { virtual void updateSampler(uint32_t binding, uint32 dstArrayIndex, Gfx::PSampler samplerState) override; virtual void updateTexture(uint32_t binding, Gfx::PTexture texture, Gfx::PSampler sampler = nullptr) override; virtual void updateTexture(uint32 binding, uint32 dstArrayIndex, Gfx::PTexture texture) override; - virtual void updateTextureArray(uint32_t binding, Array texture) override; + virtual void updateTextureArray(uint32_t binding, Array texture) override; + virtual void updateSamplerArray(uint32_t binding, Array samplers) override; constexpr bool isCurrentlyInUse() const { return currentlyInUse; } constexpr void allocate() { currentlyInUse = true; } @@ -70,7 +71,7 @@ class DescriptorSet : public Gfx::DescriptorSet, public CommandBoundResource { // since the layout is fixed, trying to bind a texture to a buffer // would not work anyways, so casts should be safe // Array cachedData; - Array boundResources; + Array> boundResources; VkDescriptorSet setHandle; PGraphics graphics; PDescriptorPool owner; diff --git a/src/Engine/Graphics/Vulkan/Query.cpp b/src/Engine/Graphics/Vulkan/Query.cpp index 29de33b..559450f 100644 --- a/src/Engine/Graphics/Vulkan/Query.cpp +++ b/src/Engine/Graphics/Vulkan/Query.cpp @@ -51,13 +51,18 @@ void QueryPool::end() { // sizeof(uint64) * currentQuery, resultsStride + sizeof(uint64), // VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT); graphics->getGraphicsCommands()->submitCommands(); + std::unique_lock l(queryMutex); currentQuery = (currentQuery + 1) % numQueries; + queryCV.notify_all(); } void QueryPool::getQueryResults(Array& results) { //uint64 numInts = resultsStride / sizeof(uint64); while (currentQuery == pendingQuery) - ; + { + std::unique_lock l(queryMutex); + queryCV.wait(l); + } results.resize(resultsStride/ sizeof(uint64)); vkGetQueryPoolResults(graphics->getDevice(), handle, pendingQuery, 1, resultsStride, results.data(), resultsStride, VK_QUERY_RESULT_WAIT_BIT | VK_QUERY_RESULT_64_BIT); diff --git a/src/Engine/Graphics/Vulkan/Query.h b/src/Engine/Graphics/Vulkan/Query.h index eb6ccad..31879b0 100644 --- a/src/Engine/Graphics/Vulkan/Query.h +++ b/src/Engine/Graphics/Vulkan/Query.h @@ -24,6 +24,8 @@ class QueryPool { uint32 currentQuery = 0; uint32 numQueries; uint32 resultsStride; + std::mutex queryMutex; + std::condition_variable queryCV; }; class OcclusionQuery : public Gfx::OcclusionQuery, public QueryPool { public: diff --git a/src/Engine/Graphics/Vulkan/Resources.cpp b/src/Engine/Graphics/Vulkan/Resources.cpp index bc583dc..e9a56cd 100644 --- a/src/Engine/Graphics/Vulkan/Resources.cpp +++ b/src/Engine/Graphics/Vulkan/Resources.cpp @@ -4,7 +4,6 @@ #include "Graphics.h" #include "Window.h" - using namespace Seele; using namespace Seele::Vulkan; @@ -76,7 +75,11 @@ DestructionManager::DestructionManager(PGraphics graphics) : graphics(graphics) DestructionManager::~DestructionManager() {} -void DestructionManager::queueResourceForDestruction(OCommandBoundResource resource) { resources.add(std::move(resource)); } +void DestructionManager::queueResourceForDestruction(OCommandBoundResource resource) { + if (resource->isCurrentlyBound()) { + resources.add(std::move(resource)); + } +} void DestructionManager::notifyCommandComplete() { for (size_t i = 0; i < resources.size(); ++i) { diff --git a/src/Engine/Material/Material.cpp b/src/Engine/Material/Material.cpp index c9e6f2a..bc90b05 100644 --- a/src/Engine/Material/Material.cpp +++ b/src/Engine/Material/Material.cpp @@ -20,11 +20,11 @@ Array Material::materials; Material::Material() {} -Material::Material(Gfx::PGraphics graphics, uint32 numTextures, uint32 numSamplers, uint32 numFloats, std::string materialName, - Array expressions, Array parameter, MaterialNode brdf) - : graphics(graphics), numTextures(numTextures), numSamplers(numSamplers), numFloats(numFloats), instanceId(0), - materialName(materialName), codeExpressions(std::move(expressions)), parameters(std::move(parameter)), brdf(std::move(brdf)), - materialId(materialIdCounter++) { +Material::Material(Gfx::PGraphics graphics, uint32 numTextures, uint32 numSamplers, uint32 numFloats, bool twoSided, float opacity, + std::string materialName, Array expressions, Array parameter, MaterialNode brdf) + : graphics(graphics), numTextures(numTextures), numSamplers(numSamplers), numFloats(numFloats), twoSided(twoSided), opacity(opacity), + instanceId(0), materialName(materialName), codeExpressions(std::move(expressions)), parameters(std::move(parameter)), + brdf(std::move(brdf)), materialId(materialIdCounter++) { if (layout == nullptr) { init(graphics); } @@ -75,13 +75,15 @@ void Material::updateDescriptor() { Gfx::SE_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); layout->reset(); set = layout->allocateDescriptorSet(); - for (uint32 i = 0; i < textures.size(); ++i) - { - set->updateTexture(0, i, textures[i]); + for (uint32 i = 0; i < textures.size(); ++i) { + if (textures[i] != nullptr) { + set->updateTexture(0, i, textures[i]); + } } - for (uint32 i = 0; i < samplers.size(); ++i) - { - set->updateSampler(1, i, samplers[i]); + for (uint32 i = 0; i < samplers.size(); ++i) { + if (samplers[i] != nullptr) { + set->updateSampler(1, i, samplers[i]); + } } set->updateBuffer(2, floatBuffer); set->writeChanges(); @@ -116,11 +118,16 @@ uint32 Material::addFloats(uint32 numFloats) { OMaterialInstance Material::instantiate() { return new MaterialInstance(instanceId++, graphics, codeExpressions, parameters, numTextures, numSamplers, numFloats); } +bool Material::isTwoSided() const { return twoSided; } +bool Material::hasTransparency() const { return opacity != 1.0f; } +float Material::getOpacity() const { return opacity; } void Material::save(ArchiveBuffer& buffer) const { Serialization::save(buffer, numTextures); Serialization::save(buffer, numSamplers); Serialization::save(buffer, numFloats); + Serialization::save(buffer, twoSided); + Serialization::save(buffer, opacity); Serialization::save(buffer, instanceId); Serialization::save(buffer, materialName); Serialization::save(buffer, codeExpressions); @@ -130,13 +137,14 @@ void Material::save(ArchiveBuffer& buffer) const { void Material::load(ArchiveBuffer& buffer) { graphics = buffer.getGraphics(); - if (layout == nullptr) - { + if (layout == nullptr) { init(graphics); } Serialization::load(buffer, numTextures); Serialization::load(buffer, numSamplers); Serialization::load(buffer, numFloats); + Serialization::load(buffer, twoSided); + Serialization::load(buffer, opacity); Serialization::load(buffer, instanceId); Serialization::load(buffer, materialName); Serialization::load(buffer, codeExpressions); @@ -167,4 +175,3 @@ void Material::compile() { codeStream << "};\n"; graphics->getShaderCompiler()->registerMaterial(this); } - diff --git a/src/Engine/Material/Material.h b/src/Engine/Material/Material.h index 07b427e..966a914 100644 --- a/src/Engine/Material/Material.h +++ b/src/Engine/Material/Material.h @@ -10,7 +10,7 @@ DECLARE_NAME_REF(Gfx, Sampler) class Material { public: Material(); - Material(Gfx::PGraphics graphics, uint32 numTextures, uint32 numSamplers, uint32 numFloats, + Material(Gfx::PGraphics graphics, uint32 numTextures, uint32 numSamplers, uint32 numFloats, bool twoSided, float opacity, std::string materialName, Array expressions, Array parameter, MaterialNode brdf); ~Material(); static void init(Gfx::PGraphics graphics); @@ -23,8 +23,14 @@ class Material { static uint32 addTextures(uint32 numTextures); static uint32 addSamplers(uint32 numSamplers); static uint32 addFloats(uint32 numFloats); + OMaterialInstance instantiate(); const std::string& getName() const { return materialName; } + + bool isTwoSided() const; + bool hasTransparency() const; + float getOpacity() const; + constexpr uint64 getId() const { return materialId; } static constexpr const PMaterial findMaterialById(uint64 id) { return materials[id]; } @@ -41,6 +47,8 @@ class Material { uint64 instanceId; uint64 materialId; std::string materialName; + bool twoSided; + float opacity; Array codeExpressions; Array parameters; MaterialNode brdf;