diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index 30ef259d0..5e559baa0 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -2758,139 +2758,23 @@ void RenderMeshes( // Purpose of InstancedBatch: // The RenderQueue is sorted by meshIndex. There can be multiple instances for a single meshIndex, - // and the InstancedBatchArray contains this information. The array size will be the unique mesh count here. + // but all instances of a single mesh will be rendered by 1 draw call struct InstancedBatch { uint32_t meshIndex; int instanceCount; uint32_t dataOffset; uint8_t userStencilRefOverride; - uint8_t forceAlphatestForDithering; // padded bool + bool forceAlphatestForDithering; AABB aabb; }; - InstancedBatch* instancedBatchArray = nullptr; - int instancedBatchCount = 0; - - // The following loop is writing the instancing batches to a GPUBuffer: - size_t prevMeshIndex = ~0; - uint8_t prevUserStencilRefOverride = 0; - uint32_t instanceCount = 0; - for (uint32_t batchID = 0; batchID < renderQueue.batchCount; ++batchID) // Do not break out of this loop! - { - const RenderBatch& batch = renderQueue.batchArray[batchID]; - const uint32_t meshIndex = batch.GetMeshIndex(); - const uint32_t instanceIndex = batch.GetInstanceIndex(); - const ObjectComponent& instance = vis.scene->objects[instanceIndex]; - const AABB& instanceAABB = vis.scene->aabb_objects[instanceIndex]; - const uint8_t userStencilRefOverride = instance.userStencilRef; - - // When we encounter a new mesh inside the global instance array, we begin a new InstancedBatch: - if (meshIndex != prevMeshIndex || userStencilRefOverride != prevUserStencilRefOverride) - { - prevMeshIndex = meshIndex; - prevUserStencilRefOverride = userStencilRefOverride; - - instancedBatchCount++; - InstancedBatch* instancedBatch = (InstancedBatch*)GetRenderFrameAllocator(cmd).allocate(sizeof(InstancedBatch)); - instancedBatch->meshIndex = meshIndex; - instancedBatch->instanceCount = 0; - instancedBatch->dataOffset = instances.offset + instanceCount * instanceDataSize; - instancedBatch->userStencilRefOverride = userStencilRefOverride; - instancedBatch->forceAlphatestForDithering = 0; - instancedBatch->aabb = AABB(); - if (instancedBatchArray == nullptr) - { - instancedBatchArray = instancedBatch; - } - } - - InstancedBatch& current_batch = instancedBatchArray[instancedBatchCount - 1]; - - float dither = instance.GetTransparency(); - - if (instance.IsImpostorPlacement()) - { - float distance = wiMath::Distance(instanceAABB.getCenter(), vis.camera->Eye); - float swapDistance = instance.impostorSwapDistance; - float fadeThreshold = instance.impostorFadeThresholdRadius; - dither = std::max(0.0f, distance - swapDistance) / fadeThreshold; - } - - if (dither > 0) - { - current_batch.forceAlphatestForDithering = 1; - } - - if (forwardLightmaskRequest) - { - current_batch.aabb = AABB::Merge(current_batch.aabb, instanceAABB); - } - - const XMFLOAT4X4& worldMatrix = instance.transform_index >= 0 ? vis.scene->transforms[instance.transform_index].world : IDENTITYMATRIX; - - for (uint32_t frustum_index = 0; frustum_index < frustum_count; ++frustum_index) - { - if (frusta != nullptr && !frusta[frustum_index].CheckBoxFast(instanceAABB)) - { - // In case multiple cameras were provided and no intersection detected with frustum, we don't add the instance for the face: - continue; - } - - // Write into actual GPU-buffer: - switch (instanceRequest) - { - default: - case INSTANCETYPE_MATRIX_USERDATA: - ((volatile Instance_MATRIX_USERDATA*)instances.data)[instanceCount].instance.Create(worldMatrix, instance.color, dither, frustum_index, instance.emissiveColor); - break; - case INSTANCETYPE_MATRIX_USERDATA_ATLAS: - ((volatile Instance_MATRIX_USERDATA_ATLAS*)instances.data)[instanceCount].instance.Create(worldMatrix, instance.color, dither, frustum_index, instance.emissiveColor); - { - XMFLOAT4 lightMapMulAdd; - if (instance.lightmap.IsValid()) - { - auto rect = instance.lightmap_rect; - - // eliminate border expansion: - rect.x += Scene::atlasClampBorder; - rect.y += Scene::atlasClampBorder; - rect.w -= Scene::atlasClampBorder * 2; - rect.h -= Scene::atlasClampBorder * 2; - - lightMapMulAdd = XMFLOAT4( - (float)rect.w / (float)lightmap_desc.Width, - (float)rect.h / (float)lightmap_desc.Height, - (float)rect.x / (float)lightmap_desc.Width, - (float)rect.y / (float)lightmap_desc.Height - ); - } - else - { - lightMapMulAdd = XMFLOAT4(0, 0, 0, 0); - } - ((volatile Instance_MATRIX_USERDATA_ATLAS*)instances.data)[instanceCount].instanceAtlas.Create(lightMapMulAdd); - } - break; - case INSTANCETYPE_MATRIX_USERDATA_MATRIXPREV: - ((volatile Instance_MATRIX_USERDATA_MATRIXPREV*)instances.data)[instanceCount].instance.Create(worldMatrix, instance.color, dither, frustum_index, instance.emissiveColor); - ((volatile Instance_MATRIX_USERDATA_MATRIXPREV*)instances.data)[instanceCount].instancePrev.Create(instance.prev_transform_index >= 0 ? vis.scene->prev_transforms[instance.prev_transform_index].world_prev : IDENTITYMATRIX); - break; - } - - current_batch.instanceCount++; // next instance in current InstancedBatch - instanceCount++; - } - - } // Render instanced batches: - PRIMITIVETOPOLOGY prevTOPOLOGY = TRIANGLELIST; - for (int instancedBatchID = 0; instancedBatchID < instancedBatchCount; ++instancedBatchID) - { - const InstancedBatch& instancedBatch = instancedBatchArray[instancedBatchID]; + auto flush_render_batch = [&](const InstancedBatch& instancedBatch) { + if (instancedBatch.instanceCount <= 0) + return; + const MeshComponent& mesh = vis.scene->meshes[instancedBatch.meshIndex]; - const bool forceAlphaTestForDithering = instancedBatch.forceAlphatestForDithering != 0; - const uint8_t userStencilRefOverride = instancedBatch.userStencilRefOverride; const float tessF = mesh.GetTessellationFactor(); const bool tessellatorRequested = tessF > 0 && tessellation; @@ -2965,7 +2849,7 @@ void RenderMeshes( { continue; } - const MaterialComponent& material = *vis.scene->materials.GetComponent(subset.materialID); + const MaterialComponent& material = vis.scene->materials[subset.materialIndex]; bool subsetRenderable = renderTypeFlags & material.GetRenderTypes(); @@ -3005,7 +2889,7 @@ void RenderMeshes( else { const BLENDMODE blendMode = material.GetBlendMode(); - const bool alphatest = material.IsAlphaTestEnabled() || forceAlphaTestForDithering; + const bool alphatest = material.IsAlphaTestEnabled() || instancedBatch.forceAlphatestForDithering; OBJECTRENDERING_DOUBLESIDED doublesided = (mesh.IsDoubleSided() || material.IsDoubleSided()) ? OBJECTRENDERING_DOUBLESIDED_ENABLED : OBJECTRENDERING_DOUBLESIDED_DISABLED; pso = &PSO_object[material.shaderType][renderPass][blendMode][doublesided][tessellatorRequested][alphatest]; @@ -3025,7 +2909,7 @@ void RenderMeshes( } STENCILREF engineStencilRef = material.engineStencilRef; - uint8_t userStencilRef = userStencilRefOverride > 0 ? userStencilRefOverride : material.userStencilRef; + uint8_t userStencilRef = instancedBatch.userStencilRefOverride > 0 ? instancedBatch.userStencilRefOverride : material.userStencilRef; uint32_t stencilRef = CombineStencilrefs(engineStencilRef, userStencilRef); device->BindStencilRef(stencilRef, cmd); @@ -3110,9 +2994,112 @@ void RenderMeshes( device->BindPipelineState(pso, cmd); device->DrawIndexedInstanced(subset.indexCount, instancedBatch.instanceCount, subset.indexOffset, 0, 0, cmd); } + }; + + InstancedBatch instancedBatch = {}; + + // The following loop is writing the instancing batches to a GPUBuffer: + uint32_t instanceCount = 0; + for (uint32_t batchID = 0; batchID < renderQueue.batchCount; ++batchID) // Do not break out of this loop! + { + const RenderBatch& batch = renderQueue.batchArray[batchID]; + const uint32_t meshIndex = batch.GetMeshIndex(); + const uint32_t instanceIndex = batch.GetInstanceIndex(); + const ObjectComponent& instance = vis.scene->objects[instanceIndex]; + const AABB& instanceAABB = vis.scene->aabb_objects[instanceIndex]; + const uint8_t userStencilRefOverride = instance.userStencilRef; + + // When we encounter a new mesh inside the global instance array, we begin a new InstancedBatch: + if (meshIndex != instancedBatch.meshIndex || userStencilRefOverride != instancedBatch.userStencilRefOverride) + { + flush_render_batch(instancedBatch); + + instancedBatch.meshIndex = meshIndex; + instancedBatch.instanceCount = 0; + instancedBatch.dataOffset = instances.offset + instanceCount * instanceDataSize; + instancedBatch.userStencilRefOverride = userStencilRefOverride; + instancedBatch.forceAlphatestForDithering = 0; + instancedBatch.aabb = AABB(); + } + + float dither = instance.GetTransparency(); + + if (instance.IsImpostorPlacement()) + { + float distance = wiMath::Distance(instanceAABB.getCenter(), vis.camera->Eye); + float swapDistance = instance.impostorSwapDistance; + float fadeThreshold = instance.impostorFadeThresholdRadius; + dither = std::max(0.0f, distance - swapDistance) / fadeThreshold; + } + + if (dither > 0) + { + instancedBatch.forceAlphatestForDithering = 1; + } + + if (forwardLightmaskRequest) + { + instancedBatch.aabb = AABB::Merge(instancedBatch.aabb, instanceAABB); + } + + const XMFLOAT4X4& worldMatrix = instance.transform_index >= 0 ? vis.scene->transforms[instance.transform_index].world : IDENTITYMATRIX; + + for (uint32_t frustum_index = 0; frustum_index < frustum_count; ++frustum_index) + { + if (frusta != nullptr && !frusta[frustum_index].CheckBoxFast(instanceAABB)) + { + // In case multiple cameras were provided and no intersection detected with frustum, we don't add the instance for the face: + continue; + } + + // Write into actual GPU-buffer: + switch (instanceRequest) + { + default: + case INSTANCETYPE_MATRIX_USERDATA: + ((volatile Instance_MATRIX_USERDATA*)instances.data)[instanceCount].instance.Create(worldMatrix, instance.color, dither, frustum_index, instance.emissiveColor); + break; + case INSTANCETYPE_MATRIX_USERDATA_ATLAS: + ((volatile Instance_MATRIX_USERDATA_ATLAS*)instances.data)[instanceCount].instance.Create(worldMatrix, instance.color, dither, frustum_index, instance.emissiveColor); + { + XMFLOAT4 lightMapMulAdd; + if (instance.lightmap.IsValid()) + { + auto rect = instance.lightmap_rect; + + // eliminate border expansion: + rect.x += Scene::atlasClampBorder; + rect.y += Scene::atlasClampBorder; + rect.w -= Scene::atlasClampBorder * 2; + rect.h -= Scene::atlasClampBorder * 2; + + lightMapMulAdd = XMFLOAT4( + (float)rect.w / (float)lightmap_desc.Width, + (float)rect.h / (float)lightmap_desc.Height, + (float)rect.x / (float)lightmap_desc.Width, + (float)rect.y / (float)lightmap_desc.Height + ); + } + else + { + lightMapMulAdd = XMFLOAT4(0, 0, 0, 0); + } + ((volatile Instance_MATRIX_USERDATA_ATLAS*)instances.data)[instanceCount].instanceAtlas.Create(lightMapMulAdd); + } + break; + case INSTANCETYPE_MATRIX_USERDATA_MATRIXPREV: + ((volatile Instance_MATRIX_USERDATA_MATRIXPREV*)instances.data)[instanceCount].instance.Create(worldMatrix, instance.color, dither, frustum_index, instance.emissiveColor); + ((volatile Instance_MATRIX_USERDATA_MATRIXPREV*)instances.data)[instanceCount].instancePrev.Create(instance.prev_transform_index >= 0 ? vis.scene->prev_transforms[instance.prev_transform_index].world_prev : IDENTITYMATRIX); + break; + } + + instancedBatch.instanceCount++; // next instance in current InstancedBatch + instanceCount++; // next instance in GPU allocation + } + } - GetRenderFrameAllocator(cmd).free(sizeof(InstancedBatch) * instancedBatchCount); + flush_render_batch(instancedBatch); device->EventEnd(cmd); } diff --git a/WickedEngine/wiScene.cpp b/WickedEngine/wiScene.cpp index a5e8ba96f..b908b4bd6 100644 --- a/WickedEngine/wiScene.cpp +++ b/WickedEngine/wiScene.cpp @@ -2770,13 +2770,14 @@ namespace wiScene std::swap(mesh.streamoutBuffer_POS, mesh.vertexBuffer_PRE); } - if (mesh.BLAS.IsValid()) + uint32_t subsetIndex = 0; + for (auto& subset : mesh.subsets) { - uint32_t subsetIndex = 0; - for (auto& subset : mesh.subsets) + const MaterialComponent* material = materials.GetComponent(subset.materialID); + if (material != nullptr) { - const MaterialComponent* material = materials.GetComponent(subset.materialID); - if (material != nullptr) + subset.materialIndex = (uint32_t)materials.GetIndex(subset.materialID); + if (mesh.BLAS.IsValid()) { auto& geometry = mesh.BLAS.desc.bottomlevel.geometries[subsetIndex]; uint32_t flags = geometry._flags; @@ -2798,9 +2799,16 @@ namespace wiScene geometry.triangles.vertexBuffer = mesh.streamoutBuffer_POS; } } - subsetIndex++; } + else + { + subset.materialIndex = 0; + } + subsetIndex++; + } + if (mesh.BLAS.IsValid()) + { if (mesh.dirty_morph) { mesh.BLAS_state = MeshComponent::BLAS_STATE_NEEDS_REBUILD; diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h index 7955cb6e0..5a147537c 100644 --- a/WickedEngine/wiScene.h +++ b/WickedEngine/wiScene.h @@ -344,6 +344,9 @@ namespace wiScene wiECS::Entity materialID = wiECS::INVALID_ENTITY; uint32_t indexOffset = 0; uint32_t indexCount = 0; + + // Non-serialized attributes: + uint32_t materialIndex = 0; }; std::vector subsets; diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 1c6fc4565..4c080a672 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wiVersion // minor features, major updates, breaking compatibility changes const int minor = 56; // minor bug fixes, alterations, refactors, updates - const int revision = 30; + const int revision = 31; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);