This commit is contained in:
SirLynix
2022-11-19 17:10:27 +01:00
committed by Jérôme Leclercq
parent 4a10c1f8fe
commit e990a320cc
54 changed files with 618 additions and 154 deletions

View File

@@ -8,7 +8,6 @@
#include <Nazara/Graphics/ElementRendererRegistry.hpp>
#include <Nazara/Graphics/FrameGraph.hpp>
#include <Nazara/Graphics/FramePipeline.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Graphics/InstancedRenderable.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Renderer/RenderFrame.hpp>
@@ -16,15 +15,16 @@
namespace Nz
{
DepthPipelinePass::DepthPipelinePass(FramePipeline& owner, ElementRendererRegistry& elementRegistry, AbstractViewer* viewer) :
DepthPipelinePass::DepthPipelinePass(FramePipeline& owner, ElementRendererRegistry& elementRegistry, AbstractViewer* viewer, std::size_t passIndex, std::string passName) :
m_passIndex(passIndex),
m_lastVisibilityHash(0),
m_passName(std::move(passName)),
m_viewer(viewer),
m_elementRegistry(elementRegistry),
m_pipeline(owner),
m_rebuildCommandBuffer(false),
m_rebuildElements(false)
{
m_depthPassIndex = Graphics::Instance()->GetMaterialPassRegistry().GetPassIndex("DepthPass");
}
void DepthPipelinePass::Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector<FramePipelinePass::VisibleRenderable>& visibleRenderables, std::size_t visibilityHash)
@@ -42,7 +42,7 @@ namespace Nz
renderableData.worldInstance
};
renderableData.instancedRenderable->BuildElement(m_elementRegistry, elementData, m_depthPassIndex, m_renderElements);
renderableData.instancedRenderable->BuildElement(m_elementRegistry, elementData, m_passIndex, m_renderElements);
}
m_renderQueueRegistry.Clear();
@@ -105,7 +105,7 @@ namespace Nz
void DepthPipelinePass::RegisterMaterialInstance(const MaterialInstance& materialInstance)
{
if (!materialInstance.HasPass(m_depthPassIndex))
if (!materialInstance.HasPass(m_passIndex))
return;
auto it = m_materialInstances.find(&materialInstance);
@@ -114,7 +114,7 @@ namespace Nz
auto& matPassEntry = m_materialInstances[&materialInstance];
matPassEntry.onMaterialInstancePipelineInvalidated.Connect(materialInstance.OnMaterialInstancePipelineInvalidated, [=](const MaterialInstance*, std::size_t passIndex)
{
if (passIndex != m_depthPassIndex)
if (passIndex != m_passIndex)
return;
m_rebuildElements = true;
@@ -131,7 +131,7 @@ namespace Nz
FramePass& DepthPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t depthBufferIndex)
{
FramePass& depthPrepass = frameGraph.AddPass("Depth pre-pass");
FramePass& depthPrepass = frameGraph.AddPass(m_passName);
depthPrepass.SetDepthStencilOutput(depthBufferIndex);
depthPrepass.SetDepthStencilClear(1.f, 0);

View File

@@ -177,11 +177,13 @@ namespace Nz
std::size_t ForwardFramePipeline::RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder)
{
std::size_t depthPassIndex = Graphics::Instance()->GetMaterialPassRegistry().GetPassIndex("DepthPass");
std::size_t viewerIndex;
auto& viewerData = *m_viewerPool.Allocate(viewerIndex);
viewerData.renderOrder = renderOrder;
viewerData.debugDrawPass = std::make_unique<DebugDrawPipelinePass>(*this, viewerInstance);
viewerData.depthPrepass = std::make_unique<DepthPipelinePass>(*this, m_elementRegistry, viewerInstance);
viewerData.depthPrepass = std::make_unique<DepthPipelinePass>(*this, m_elementRegistry, viewerInstance, depthPassIndex, "Depth pre-pass");
viewerData.forwardPass = std::make_unique<ForwardPipelinePass>(*this, m_elementRegistry, viewerInstance);
viewerData.viewer = viewerInstance;
viewerData.onTransferRequired.Connect(viewerInstance->GetViewerInstance().OnTransferRequired, [this](TransferInterface* transferInterface)
@@ -211,6 +213,20 @@ namespace Nz
return worldInstanceIndex;
}
const Light* ForwardFramePipeline::RetrieveLight(std::size_t lightIndex) const
{
return m_lightPool.RetrieveFromIndex(lightIndex)->light.get();
}
const Texture* ForwardFramePipeline::RetrieveLightShadowmap(std::size_t lightIndex) const
{
if (!m_shadowCastingLights.UnboundedTest(lightIndex))
return nullptr;
std::size_t shadowmapIndex = m_lightPool.RetrieveFromIndex(lightIndex)->shadowMapAttachmentIndex;
return m_bakedFrameGraph.GetAttachmentTexture(shadowmapIndex).get();
}
void ForwardFramePipeline::Render(RenderFrame& renderFrame)
{
m_currentRenderFrame = &renderFrame;
@@ -355,13 +371,17 @@ namespace Nz
m_visibleLights.clear();
for (const LightData& lightData : m_lightPool)
for (auto it = m_lightPool.begin(); it != m_lightPool.end(); ++it)
{
const LightData& lightData = *it;
std::size_t lightIndex = it.GetIndex();
const BoundingVolumef& boundingVolume = lightData.light->GetBoundingVolume();
// TODO: Use more precise tests for point lights (frustum/sphere is cheap)
if (renderMask & lightData.renderMask && frustum.Contains(boundingVolume))
{
m_visibleLights.push_back(lightData.light.get());
m_visibleLights.push_back(lightIndex);
visibilityHash = CombineHash(visibilityHash, std::hash<const void*>()(lightData.light.get()));
}
}
@@ -575,7 +595,7 @@ namespace Nz
m_transferSet.insert(transferInterface);
});
lightData->camera->UpdateFOV(spotLight.GetOuterAngle());
lightData->camera->UpdateFOV(spotLight.GetOuterAngle() * 2.f);
lightData->camera->UpdateZFar(spotLight.GetRadius());
lightData->camera->UpdateViewport(Recti(0, 0, SafeCast<int>(shadowMapSize), SafeCast<int>(shadowMapSize)));
@@ -584,13 +604,15 @@ namespace Nz
if (!lightData->pass)
{
lightData->pass = std::make_unique<DepthPipelinePass>(*this, m_elementRegistry, lightData->camera.get());
std::size_t shadowPassIndex = Graphics::Instance()->GetMaterialPassRegistry().GetPassIndex("ShadowPass");
lightData->pass = std::make_unique<DepthPipelinePass>(*this, m_elementRegistry, lightData->camera.get(), shadowPassIndex, "Spot shadowmap");
for (RenderableData& renderable : m_renderablePool)
{
std::size_t matCount = renderable.renderable->GetMaterialCount();
for (std::size_t i = 0; i < matCount; ++i)
for (std::size_t j = 0; j < matCount; ++j)
{
if (MaterialInstance* mat = renderable.renderable->GetMaterial(i).get())
if (MaterialInstance* mat = renderable.renderable->GetMaterial(j).get())
lightData->pass->RegisterMaterialInstance(*mat);
}
}

View File

@@ -31,7 +31,7 @@ namespace Nz
m_lightUboPool = std::make_shared<LightUboPool>();
}
void ForwardPipelinePass::Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector<FramePipelinePass::VisibleRenderable>& visibleRenderables, const std::vector<const Light*>& visibleLights, std::size_t visibilityHash)
void ForwardPipelinePass::Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector<FramePipelinePass::VisibleRenderable>& visibleRenderables, const std::vector<std::size_t>& visibleLights, std::size_t visibilityHash)
{
if (m_lastVisibilityHash != visibilityHash || m_rebuildElements) //< FIXME
{
@@ -65,17 +65,22 @@ namespace Nz
// Select lights
m_renderableLights.clear();
for (const Light* light : visibleLights)
for (std::size_t lightIndex : visibleLights)
{
const Light* light = m_pipeline.RetrieveLight(lightIndex);
const BoundingVolumef& boundingVolume = light->GetBoundingVolume();
if (boundingVolume.Intersect(renderableBoundingVolume.aabb))
m_renderableLights.push_back(light);
{
float contributionScore = light->ComputeContributionScore(renderableBoundingVolume);
m_renderableLights.push_back({ light, lightIndex, contributionScore });
}
}
// Sort lights
std::sort(m_renderableLights.begin(), m_renderableLights.end(), [&](const Light* lhs, const Light* rhs)
std::sort(m_renderableLights.begin(), m_renderableLights.end(), [&](const RenderableLight& lhs, const RenderableLight& rhs)
{
return lhs->ComputeContributionScore(renderableBoundingVolume) < rhs->ComputeContributionScore(renderableBoundingVolume);
return lhs.contributionScore < rhs.contributionScore;
});
std::size_t lightCount = std::min(m_renderableLights.size(), MaxLightCountPerDraw);
@@ -83,7 +88,7 @@ namespace Nz
LightKey lightKey;
lightKey.fill(nullptr);
for (std::size_t i = 0; i < lightCount; ++i)
lightKey[i] = m_renderableLights[i];
lightKey[i] = m_renderableLights[i].light;
RenderBufferView lightUboView;
@@ -130,7 +135,7 @@ namespace Nz
UInt8* lightPtr = static_cast<UInt8*>(lightDataPtr) + lightOffsets.lightsOffset;
for (std::size_t i = 0; i < lightCount; ++i)
{
m_renderableLights[i]->FillLightData(lightPtr);
m_renderableLights[i].light->FillLightData(lightPtr);
lightPtr += lightOffsets.lightSize;
}
@@ -155,7 +160,15 @@ namespace Nz
for (std::size_t i = previousCount; i < m_renderElements.size(); ++i)
{
const RenderElement* element = m_renderElements[i].GetElement();
m_lightPerRenderElement.emplace(element, lightUboView);
LightPerElementData perElementData;
perElementData.lightCount = lightCount;
perElementData.lightUniformBuffer = lightUboView;
for (std::size_t i = 0; i < lightCount; ++i)
perElementData.shadowMaps[i] = m_pipeline.RetrieveLightShadowmap(m_renderableLights[i].lightIndex);
m_lightPerRenderElement.emplace(element, perElementData);
}
}
@@ -222,8 +235,25 @@ namespace Nz
auto it = lightPerRenderElement.find(elements[i]);
assert(it != lightPerRenderElement.end());
const LightPerElementData& lightData = it->second;
auto& renderStates = m_renderStates.emplace_back();
renderStates.lightData = it->second;
renderStates.lightData = lightData.lightUniformBuffer;
for (std::size_t i = 0; i < lightData.lightCount; ++i)
{
const Texture* texture = lightData.shadowMaps[i];
if (!texture)
continue;
if (texture->GetType() == ImageType::E2D)
renderStates.shadowMaps2D[i] = texture;
else
{
assert(texture->GetType() == ImageType::Cubemap);
renderStates.shadowMapsCube[i] = texture;
}
}
}
elementRenderer.Prepare(viewerInstance, *m_elementRendererData[elementType], renderFrame, elementCount, elements, m_renderStates.data());

View File

@@ -218,7 +218,7 @@ namespace Nz
{
if (auto it = m_pending.attachmentToTextures.find(depthStencilOutput); it == m_pending.attachmentToTextures.end())
{
// Special case where multiples attachements point simultaneously to the same texture
// Special case where multiples attachments point simultaneously to the same texture
m_pending.attachmentToTextures.emplace(depthStencilOutput, textureId);
auto inputIt = m_pending.attachmentLastUse.find(depthStencilInput);

View File

@@ -173,7 +173,7 @@ namespace Nz
RenderPipelineLayoutInfo layoutInfo;
layoutInfo.bindings.assign({
{
0, 0,
1, 0, 0,
ShaderBindingType::Texture,
nzsl::ShaderStageType::Fragment
}
@@ -230,7 +230,10 @@ namespace Nz
MaterialPass depthPass = forwardPass;
depthPass.options[CRC32("DepthPass")] = true;
settings.AddPass(depthPassIndex, depthPass);
settings.AddPass(shadowPassIndex, depthPass);
MaterialPass shadowPass = depthPass;
shadowPass.states.faceCulling = FaceCulling::Front;
settings.AddPass(shadowPassIndex, shadowPass);
m_defaultMaterials.basicMaterial = std::make_shared<Material>(std::move(settings), "BasicMaterial");
}
@@ -249,7 +252,10 @@ namespace Nz
MaterialPass depthPass = forwardPass;
depthPass.options[CRC32("DepthPass")] = true;
settings.AddPass(depthPassIndex, depthPass);
settings.AddPass(shadowPassIndex, depthPass);
MaterialPass shadowPass = depthPass;
shadowPass.states.faceCulling = FaceCulling::Front;
settings.AddPass(shadowPassIndex, shadowPass);
m_defaultMaterials.pbrMaterial = std::make_shared<Material>(std::move(settings), "PhysicallyBasedMaterial");
}
@@ -268,7 +274,13 @@ namespace Nz
MaterialPass depthPass = forwardPass;
depthPass.options[CRC32("DepthPass")] = true;
settings.AddPass(depthPassIndex, depthPass);
settings.AddPass(shadowPassIndex, depthPass);
MaterialPass shadowPass = depthPass;
shadowPass.states.faceCulling = FaceCulling::Front;
shadowPass.states.depthBias = true;
shadowPass.states.depthBiasConstantFactor = 0.005f;
shadowPass.states.depthBiasSlopeFactor = 0.05f;
settings.AddPass(shadowPassIndex, shadowPass);
m_defaultMaterials.phongMaterial = std::make_shared<Material>(std::move(settings), "PhongMaterial");
}
@@ -301,6 +313,38 @@ namespace Nz
void Graphics::BuildDefaultTextures()
{
// Depth textures (white but with a depth format)
{
PixelFormat depthFormat = PixelFormat::Undefined;
for (PixelFormat depthStencilCandidate : { PixelFormat::Depth16, PixelFormat::Depth24, PixelFormat::Depth32F })
{
if (m_renderDevice->IsTextureFormatSupported(depthStencilCandidate, TextureUsage::ShaderSampling))
{
depthFormat = depthStencilCandidate;
break;
}
}
if (depthFormat == PixelFormat::Undefined)
throw std::runtime_error("couldn't find a sampling-compatible depth pixel format");
TextureInfo texInfo;
texInfo.width = texInfo.height = texInfo.depth = texInfo.mipmapLevel = 1;
texInfo.pixelFormat = depthFormat;
std::array<UInt8, 6> whitePixels = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
for (std::size_t i = 0; i < ImageTypeCount; ++i)
{
texInfo.type = static_cast<ImageType>(i);
if (texInfo.type == ImageType::E3D)
continue;
m_defaultTextures.depthTextures[i] = m_renderDevice->InstantiateTexture(texInfo);
m_defaultTextures.depthTextures[i]->Update(whitePixels.data());
}
}
// White texture 2D
{
TextureInfo texInfo;

View File

@@ -94,6 +94,12 @@ namespace Nz
if (auto it = block->uniformBlocks.find("ViewerData"); it != block->uniformBlocks.end())
m_engineShaderBindings[UnderlyingCast(EngineShaderBinding::ViewerDataUbo)] = it->second.bindingIndex;
if (auto it = block->samplers.find("ShadowMaps2D"); it != block->samplers.end())
m_engineShaderBindings[UnderlyingCast(EngineShaderBinding::Shadowmap2D)] = it->second.bindingIndex;
if (auto it = block->samplers.find("ShadowMapsCube"); it != block->samplers.end())
m_engineShaderBindings[UnderlyingCast(EngineShaderBinding::ShadowmapCube)] = it->second.bindingIndex;
if (auto it = block->uniformBlocks.find("SkeletalData"); it != block->uniformBlocks.end())
m_engineShaderBindings[UnderlyingCast(EngineShaderBinding::SkeletalDataUbo)] = it->second.bindingIndex;

View File

@@ -21,6 +21,7 @@ namespace Nz
lightData.lightMemberOffsets.parameter2 = lightStruct.AddField(nzsl::StructFieldType::Float4);
lightData.lightMemberOffsets.parameter3 = lightStruct.AddField(nzsl::StructFieldType::Float4);
lightData.lightMemberOffsets.shadowMappingFlag = lightStruct.AddField(nzsl::StructFieldType::Bool1);
lightData.lightMemberOffsets.viewProjMatrix = lightStruct.AddMatrix(nzsl::StructFieldType::Float1, 4, 4, true);
lightData.lightSize = lightStruct.GetAlignedSize();

View File

@@ -15,8 +15,8 @@ struct VertOut
}
const vertPos = array[vec2[f32]](
vec2[f32](-1.0, 1.0),
vec2[f32](-1.0, -3.0),
vec2[f32](-1.0, 1.0),
vec2[f32]( 3.0, 1.0)
);

View File

@@ -13,7 +13,8 @@ struct Light
parameter1: vec4[f32],
parameter2: vec4[f32],
parameter3: vec4[f32],
hasShadowMapping: u32
hasShadowMapping: u32,
viewProjMatrix: mat4[f32]
}
[export]

View File

@@ -47,6 +47,7 @@ const HasTangent = (VertexTangentLoc >= 0);
const HasUV = (VertexUvLoc >= 0);
const HasNormalMapping = HasNormalTexture && HasNormal && HasTangent && !DepthPass;
const HasSkinning = (VertexJointIndicesLoc >= 0 && VertexJointWeightsLoc >= 0);
const HasLighting = HasNormal && !DepthPass;
[layout(std140)]
struct MaterialSettings
@@ -96,8 +97,8 @@ external
[tag("ViewerData")] viewerData: uniform[ViewerData],
[tag("SkeletalData")] skeletalData: uniform[SkeletalData],
[tag("LightData")] lightData: uniform[LightData],
[tag("ShadowMaps2D")] shadowMaps2D: array[sampler2D[f32], MaxLightCount],
[tag("ShadowMapsCube")] shadowMapsCube: array[samplerCube[f32], MaxLightCount]
[tag("ShadowMaps2D")] shadowMaps2D: array[depth_sampler2D[f32], MaxLightCount],
[tag("ShadowMapsCube")] shadowMapsCube: array[depth_sampler_cube[f32], MaxLightCount]
}
struct VertToFrag
@@ -107,6 +108,7 @@ struct VertToFrag
[location(2), cond(HasColor)] color: vec4[f32],
[location(3), cond(HasNormal)] normal: vec3[f32],
[location(4), cond(HasNormalMapping)] tangent: vec3[f32],
[location(5), cond(HasLighting)] lightProjPos: array[vec4[f32], MaxLightCount],
[builtin(position)] position: vec4[f32],
}
@@ -139,7 +141,7 @@ fn main(input: VertToFrag) -> FragOut
discard;
}
const if (HasNormal && !DepthPass)
const if (HasLighting)
{
let lightAmbient = vec3[f32](0.0, 0.0, 0.0);
let lightDiffuse = vec3[f32](0.0, 0.0, 0.0);
@@ -225,17 +227,41 @@ fn main(input: VertToFrag) -> FragOut
let attenuationFactor = max(1.0 - dist * lightInvRadius, 0.0);
attenuationFactor *= max((curAngle - lightOuterAngle) / innerMinusOuterAngle, 0.0);
lightAmbient += attenuationFactor * light.color.rgb * lightAmbientFactor * settings.AmbientColor.rgb;
let lambert = max(dot(normal, -lightToPosNorm), 0.0);
lightDiffuse += attenuationFactor * lambert * light.color.rgb * lightDiffuseFactor;
let reflection = reflect(lightToPosNorm, normal);
let specFactor = max(dot(reflection, eyeVec), 0.0);
specFactor = pow(specFactor, settings.Shininess);
lightSpecular += attenuationFactor * specFactor * light.color.rgb;
let shadowFactor = 0.0;
if (true) //< TODO: HasShadowMapping
{
let shadowTexSize = 1.0 / 512.0; //< FIXME
let offsetArray = array[vec2[f32], 9](
vec2[f32](-1.0, -1.0),
vec2[f32](-1.0, 0.0),
vec2[f32](-1.0, 1.0),
vec2[f32](0.0, -1.0),
vec2[f32](0.0, 0.0),
vec2[f32](0.0, 1.0),
vec2[f32](1.0, -1.0),
vec2[f32](1.0, 0.0),
vec2[f32](1.0, 1.0)
);
for offset in offsetArray
{
let shadowCoords = input.lightProjPos[i].xyz / input.lightProjPos[i].w;
shadowCoords.xy += offset * shadowTexSize;
shadowFactor += shadowMaps2D[i].SampleDepthComp(shadowCoords.xy, shadowCoords.z).r;
}
shadowFactor /= 9.0;
}
lightAmbient += attenuationFactor * light.color.rgb * lightAmbientFactor * settings.AmbientColor.rgb;
lightDiffuse += shadowFactor * attenuationFactor * lambert * light.color.rgb * lightDiffuseFactor;
lightSpecular += shadowFactor * attenuationFactor * specFactor * light.color.rgb;
}
}
@@ -382,5 +408,11 @@ fn main(input: VertIn) -> VertToFrag
const if (HasNormalMapping)
output.tangent = rotationMatrix * input.tangent;
const if (HasLighting)
{
for i in u32(0) -> lightData.lightCount
output.lightProjPos[i] = lightData.lights[i].viewProjMatrix * worldPosition;
}
return output;
}

View File

@@ -73,16 +73,14 @@ namespace Nz
else
throw std::runtime_error("unexpected type " + ToString(varType));
for (UInt32 i = 0; i < arraySize; ++i)
{
// TODO: Get more precise shader stage type
m_pipelineLayoutInfo.bindings.push_back({
bindingSet, // setIndex
bindingIndex + i, // bindingIndex
bindingType, // type
nzsl::ShaderStageType_All // shaderStageFlags
});
}
// TODO: Get more precise shader stage type
m_pipelineLayoutInfo.bindings.push_back({
arraySize, // arraySize
bindingIndex, // bindingIndex
bindingSet, // setIndex
bindingType, // type
nzsl::ShaderStageType_All // shaderStageFlags
});
if (!externalVar.tag.empty() && externalBlock)
{

View File

@@ -30,6 +30,7 @@ namespace Nz
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.parameter2) = Vector4f(m_direction.x, m_direction.y, m_direction.z, 0.f);
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.parameter3) = Vector4f(m_innerAngleCos, m_outerAngleCos, 0.f, 0.f);
AccessByOffset<UInt8&>(data, lightOffset.lightMemberOffsets.shadowMappingFlag) = 0;
AccessByOffset<Matrix4f&>(data, lightOffset.lightMemberOffsets.viewProjMatrix) = m_viewProjMatrix;
}
void SpotLight::UpdateTransform(const Vector3f& position, const Quaternionf& rotation, const Vector3f& /*scale*/)

View File

@@ -54,9 +54,16 @@ namespace Nz
currentShaderBinding = nullptr;
};
const auto& whiteTexture = Graphics::Instance()->GetDefaultTextures().whiteTextures[UnderlyingCast(ImageType::E2D)];
const auto& depthTexture2D = Graphics::Instance()->GetDefaultTextures().depthTextures[UnderlyingCast(ImageType::E2D)];
const auto& depthTextureCube = Graphics::Instance()->GetDefaultTextures().depthTextures[UnderlyingCast(ImageType::Cubemap)];
const auto& whiteTexture2D = Graphics::Instance()->GetDefaultTextures().whiteTextures[UnderlyingCast(ImageType::E2D)];
const auto& whiteTextureCube = Graphics::Instance()->GetDefaultTextures().whiteTextures[UnderlyingCast(ImageType::Cubemap)];
const auto& defaultSampler = graphics->GetSamplerCache().Get({});
TextureSamplerInfo samplerInfo;
samplerInfo.depthCompare = true;
const auto& shadowSampler = graphics->GetSamplerCache().Get(samplerInfo);
std::size_t oldDrawCallCount = data.drawCalls.size();
for (std::size_t i = 0; i < elementCount; ++i)
@@ -122,6 +129,8 @@ namespace Nz
assert(currentMaterialInstance);
m_bindingCache.clear();
m_textureBindingCache.clear();
m_textureBindingCache.reserve(renderState.shadowMaps2D.size() + renderState.shadowMapsCube.size());
currentMaterialInstance->FillShaderBinding(m_bindingCache);
const Material& material = *currentMaterialInstance->GetParentMaterial();
@@ -150,6 +159,50 @@ namespace Nz
};
}
if (UInt32 bindingIndex = material.GetEngineBindingIndex(EngineShaderBinding::Shadowmap2D); bindingIndex != Material::InvalidBindingIndex)
{
std::size_t textureBindingBaseIndex = m_textureBindingCache.size();
for (std::size_t j = 0; j < renderState.shadowMaps2D.size(); ++j)
{
const Texture* texture = renderState.shadowMaps2D[j];
if (!texture)
texture = depthTexture2D.get();
auto& textureEntry = m_textureBindingCache.emplace_back();
textureEntry.texture = texture;
textureEntry.sampler = shadowSampler.get();
}
auto& bindingEntry = m_bindingCache.emplace_back();
bindingEntry.bindingIndex = bindingIndex;
bindingEntry.content = ShaderBinding::TextureBindings {
SafeCast<UInt32>(renderState.shadowMaps2D.size()), &m_textureBindingCache[textureBindingBaseIndex]
};
}
if (UInt32 bindingIndex = material.GetEngineBindingIndex(EngineShaderBinding::ShadowmapCube); bindingIndex != Material::InvalidBindingIndex)
{
std::size_t textureBindingBaseIndex = m_textureBindingCache.size();
for (std::size_t j = 0; j < renderState.shadowMapsCube.size(); ++j)
{
const Texture* texture = renderState.shadowMapsCube[j];
if (!texture)
texture = depthTextureCube.get();
auto& textureEntry = m_textureBindingCache.emplace_back();
textureEntry.texture = texture;
textureEntry.sampler = shadowSampler.get();
}
auto& bindingEntry = m_bindingCache.emplace_back();
bindingEntry.bindingIndex = bindingIndex;
bindingEntry.content = ShaderBinding::TextureBindings {
SafeCast<UInt32>(renderState.shadowMaps2D.size()), &m_textureBindingCache[textureBindingBaseIndex]
};
}
if (UInt32 bindingIndex = material.GetEngineBindingIndex(EngineShaderBinding::SkeletalDataUbo); bindingIndex != Material::InvalidBindingIndex && currentSkeletonInstance)
{
const auto& skeletalBuffer = currentSkeletonInstance->GetSkeletalBuffer();
@@ -179,7 +232,7 @@ namespace Nz
auto& bindingEntry = m_bindingCache.emplace_back();
bindingEntry.bindingIndex = bindingIndex;
bindingEntry.content = ShaderBinding::TextureBinding{
whiteTexture.get(), defaultSampler.get()
whiteTexture2D.get(), defaultSampler.get()
};
}

View File

@@ -21,10 +21,14 @@ namespace Nz
unsigned int bindingIndex = 0;
for (const auto& binding : m_layoutInfo.bindings)
{
UInt64 bindingKey = UInt64(binding.setIndex) << 32 | UInt64(binding.bindingIndex);
for (UInt32 i = 0; i < binding.arraySize; ++i)
{
UInt64 bindingKey = UInt64(binding.setIndex) << 32 | UInt64(binding.bindingIndex + i);
m_bindingMapping[bindingKey] = bindingIndex++;
m_maxDescriptorCount = std::max<std::size_t>(m_maxDescriptorCount, binding.bindingIndex + 1);
m_bindingMapping[bindingKey] = bindingIndex++;
}
m_maxDescriptorCount = std::max<std::size_t>(m_maxDescriptorCount, binding.bindingIndex + binding.arraySize);
}
}

View File

@@ -23,7 +23,18 @@ namespace Nz
{
using DescriptorType = std::decay_t<decltype(descriptor)>;
auto bindingIt = std::find_if(layoutInfo.bindings.begin(), layoutInfo.bindings.end(), [&](const auto& binding) { return binding.setIndex == setIndex && binding.bindingIndex == bindingIndex; });
auto bindingIt = std::find_if(layoutInfo.bindings.begin(), layoutInfo.bindings.end(), [&](const auto& binding)
{
if (binding.setIndex != setIndex)
return false;
assert(binding.arraySize > 0);
if (bindingIndex < binding.bindingIndex || bindingIndex >= binding.bindingIndex + binding.arraySize)
return false;
return true;
});
if (bindingIt == layoutInfo.bindings.end())
throw std::runtime_error("invalid binding index");
@@ -88,26 +99,11 @@ namespace Nz
storageDescriptor.buffer = 0;
}
else if constexpr (std::is_same_v<T, TextureBinding>)
HandleTextureBinding(binding.bindingIndex, arg);
else if constexpr (std::is_same_v<T, TextureBindings>)
{
auto& textureDescriptor = m_owner.GetTextureDescriptor(m_poolIndex, m_bindingIndex, binding.bindingIndex);
if (const OpenGLTexture* glTexture = static_cast<const OpenGLTexture*>(arg.texture))
{
textureDescriptor.texture = glTexture->GetTexture().GetObjectId();
if (const OpenGLTextureSampler* glSampler = static_cast<const OpenGLTextureSampler*>(arg.sampler))
textureDescriptor.sampler = glSampler->GetSampler(glTexture->GetLevelCount() > 1).GetObjectId();
else
textureDescriptor.sampler = 0;
textureDescriptor.textureTarget = OpenGLTexture::ToTextureTarget(glTexture->GetType());
}
else
{
textureDescriptor.sampler = 0;
textureDescriptor.texture = 0;
textureDescriptor.textureTarget = GL::TextureTarget::Target2D;
}
for (UInt32 i = 0; i < arg.arraySize; ++i)
HandleTextureBinding(binding.bindingIndex + i, arg.textureBindings[i]);
}
else if constexpr (std::is_same_v<T, UniformBufferBinding>)
{
@@ -137,6 +133,29 @@ namespace Nz
// No OpenGL object to name
}
void OpenGLShaderBinding::HandleTextureBinding(UInt32 bindingIndex, const TextureBinding& textureBinding)
{
auto& textureDescriptor = m_owner.GetTextureDescriptor(m_poolIndex, m_bindingIndex, bindingIndex);
if (const OpenGLTexture* glTexture = static_cast<const OpenGLTexture*>(textureBinding.texture))
{
textureDescriptor.texture = glTexture->GetTexture().GetObjectId();
if (const OpenGLTextureSampler* glSampler = static_cast<const OpenGLTextureSampler*>(textureBinding.sampler))
textureDescriptor.sampler = glSampler->GetSampler(glTexture->GetLevelCount() > 1).GetObjectId();
else
textureDescriptor.sampler = 0;
textureDescriptor.textureTarget = OpenGLTexture::ToTextureTarget(glTexture->GetType());
}
else
{
textureDescriptor.sampler = 0;
textureDescriptor.texture = 0;
textureDescriptor.textureTarget = GL::TextureTarget::Target2D;
}
}
void OpenGLShaderBinding::Release()
{
m_owner.Release(*this);

View File

@@ -36,6 +36,12 @@ namespace Nz
if (samplerInfo.anisotropyLevel > 1.f)
sampler.SetParameterf(GL_TEXTURE_MAX_ANISOTROPY_EXT, samplerInfo.anisotropyLevel);
if (samplerInfo.depthCompare)
{
sampler.SetParameteri(GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
sampler.SetParameteri(GL_TEXTURE_COMPARE_FUNC, ToOpenGL(samplerInfo.depthComparison));
}
}
void OpenGLTextureSampler::UpdateDebugName(std::string_view name)

View File

@@ -565,8 +565,13 @@ namespace Nz::GL
glGetIntegerv(GL_VIEWPORT, res.data());
m_state.viewport = { res[0], res[1], res[2], res[3] };
m_state.renderStates.depthCompare = RendererComparison::Less; //< OpenGL default depth mode is GL_LESS
m_state.renderStates.frontFace = FrontFace::CounterClockwise; //< OpenGL default front face is GL_CCW
// Set default OpenGL states
m_state.renderStates.depthBuffer = false;
m_state.renderStates.depthCompare = RendererComparison::Less;
m_state.renderStates.faceCulling = FaceCulling::None;
m_state.renderStates.frontFace = FrontFace::CounterClockwise;
m_state.renderStates.scissorTest = false;
m_state.renderStates.stencilTest = false;
EnableVerticalSync(false);
@@ -671,6 +676,18 @@ namespace Nz::GL
if (!SetCurrentContext(this))
throw std::runtime_error("failed to activate context");
// Depth bias
if (renderStates.depthBias)
{
if (!NumberEquals(m_state.renderStates.depthBiasConstantFactor, renderStates.depthBiasConstantFactor) ||
!NumberEquals(m_state.renderStates.depthBiasSlopeFactor, renderStates.depthBiasSlopeFactor))
{
glPolygonOffset(renderStates.depthBiasConstantFactor, renderStates.depthBiasSlopeFactor);
m_state.renderStates.depthBiasConstantFactor = renderStates.depthBiasConstantFactor;
m_state.renderStates.depthBiasSlopeFactor = renderStates.depthBiasSlopeFactor;
}
}
// Depth compare and depth write
if (renderStates.depthBuffer)
{
@@ -687,14 +704,23 @@ namespace Nz::GL
}
}
// Face culling side
if (renderStates.faceCulling)
// Face culling
if (m_state.renderStates.faceCulling != renderStates.faceCulling)
{
if (m_state.renderStates.cullingSide != renderStates.cullingSide)
bool wasEnabled = (m_state.renderStates.faceCulling != FaceCulling::None);
bool isEnabled = (renderStates.faceCulling != FaceCulling::None);
if (isEnabled)
{
glCullFace(ToOpenGL(renderStates.cullingSide));
m_state.renderStates.cullingSide = renderStates.cullingSide;
if (!wasEnabled)
glEnable(GL_CULL_FACE);
glCullFace(ToOpenGL(renderStates.faceCulling));
}
else if (wasEnabled)
glDisable(GL_CULL_FACE);
m_state.renderStates.faceCulling = renderStates.faceCulling;
}
// Front face
@@ -811,6 +837,18 @@ namespace Nz::GL
m_state.renderStates.colorWriteMask = renderStates.colorWriteMask;
}
// Depth bias
if (m_state.renderStates.depthBias != renderStates.depthBias)
{
// TODO: Handle line and points
if (renderStates.depthBias)
glEnable(GL_POLYGON_OFFSET_FILL);
else
glDisable(GL_POLYGON_OFFSET_FILL);
m_state.renderStates.depthBias = renderStates.depthBias;
}
// Depth buffer
if (m_state.renderStates.depthBuffer != renderStates.depthBuffer)
{
@@ -835,17 +873,6 @@ namespace Nz::GL
m_state.renderStates.depthClamp = renderStates.depthClamp;
}
// Face culling
if (m_state.renderStates.faceCulling != renderStates.faceCulling)
{
if (renderStates.faceCulling)
glEnable(GL_CULL_FACE);
else
glDisable(GL_CULL_FACE);
m_state.renderStates.faceCulling = renderStates.faceCulling;
}
// Scissor test
if (m_state.renderStates.scissorTest != renderStates.scissorTest)
{

View File

@@ -39,7 +39,7 @@ namespace Nz
RenderPipelineLayoutInfo layoutInfo;
layoutInfo.bindings.assign({
{
0, 0,
1, 0, 0,
ShaderBindingType::UniformBuffer,
nzsl::ShaderStageType::Vertex
}

View File

@@ -133,8 +133,8 @@ namespace Nz
std::string appName = parameters.GetStringParameter("VkAppInfo_OverrideApplicationName").GetValueOr("Another application made with Nazara Engine");
std::string engineName = parameters.GetStringParameter("VkAppInfo_OverrideEngineName").GetValueOr("Nazara Engine - Vulkan Renderer");
UInt32 appVersion = parameters.GetIntegerParameter("VkAppInfo_OverrideApplicationVersion").GetValueOr(VK_MAKE_API_VERSION(0, 1, 0, 0));
UInt32 engineVersion = parameters.GetIntegerParameter("VkAppInfo_OverrideEngineVersion").GetValueOr(VK_MAKE_API_VERSION(0, 1, 0, 0));
UInt32 appVersion = SafeCast<UInt32>(parameters.GetIntegerParameter("VkAppInfo_OverrideApplicationVersion").GetValueOr(VK_MAKE_API_VERSION(0, 1, 0, 0)));
UInt32 engineVersion = SafeCast<UInt32>(parameters.GetIntegerParameter("VkAppInfo_OverrideEngineVersion").GetValueOr(VK_MAKE_API_VERSION(0, 1, 0, 0)));
if (auto result = parameters.GetIntegerParameter("VkAppInfo_OverrideAPIVersion"))
targetApiVersion = SafeCast<UInt32>(result.GetValue());
@@ -160,7 +160,7 @@ namespace Nz
targetApiVersion
};
VkInstanceCreateFlags createFlags = parameters.GetIntegerParameter("VkInstanceInfo_OverrideCreateFlags").GetValueOr(0);
VkInstanceCreateFlags createFlags = SafeCast<VkInstanceCreateFlags>(parameters.GetIntegerParameter("VkInstanceInfo_OverrideCreateFlags").GetValueOr(0));
std::vector<const char*> enabledLayers;

View File

@@ -159,7 +159,10 @@ namespace Nz
{
VkPipelineRasterizationStateCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
createInfo.cullMode = (pipelineInfo.faceCulling) ? ToVulkan(pipelineInfo.cullingSide) : VK_CULL_MODE_NONE;
createInfo.cullMode = ToVulkan(pipelineInfo.faceCulling);
createInfo.depthBiasEnable = pipelineInfo.depthBias;
createInfo.depthBiasConstantFactor = pipelineInfo.depthBiasConstantFactor;
createInfo.depthBiasSlopeFactor = pipelineInfo.depthBiasSlopeFactor;
createInfo.depthClampEnable = pipelineInfo.depthClamp;
createInfo.frontFace = ToVulkan(pipelineInfo.frontFace);
createInfo.lineWidth = pipelineInfo.lineWidth;

View File

@@ -69,7 +69,7 @@ namespace Nz
VkDescriptorSetLayoutBinding& layoutBinding = descriptorSetLayoutInfo.bindings.emplace_back();
layoutBinding.binding = bindingInfo.bindingIndex;
layoutBinding.descriptorCount = 1U;
layoutBinding.descriptorCount = bindingInfo.arraySize;
layoutBinding.descriptorType = ToVulkan(bindingInfo.type);
layoutBinding.pImmutableSamplers = nullptr;
layoutBinding.stageFlags = ToVulkan(bindingInfo.shaderStageFlags);

View File

@@ -15,8 +15,34 @@ namespace Nz
{
void VulkanShaderBinding::Update(const Binding* bindings, std::size_t bindingCount)
{
StackVector<VkDescriptorBufferInfo> bufferBinding = NazaraStackVector(VkDescriptorBufferInfo, bindingCount);
StackVector<VkDescriptorImageInfo> imageBinding = NazaraStackVector(VkDescriptorImageInfo, bindingCount);
std::size_t bufferBindingCount = 0;
std::size_t imageBindingCount = 0;
for (std::size_t i = 0; i < bindingCount; ++i)
{
const Binding& binding = bindings[i];
std::visit([&](auto&& arg)
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, StorageBufferBinding> || std::is_same_v<T, UniformBufferBinding>)
bufferBindingCount++;
else if constexpr (std::is_same_v<T, TextureBinding>)
imageBindingCount++;
else if constexpr (std::is_same_v<T, TextureBindings>)
imageBindingCount += arg.arraySize;
else
static_assert(AlwaysFalse<T>(), "non-exhaustive visitor");
}, binding.content);
}
NazaraAssert(bufferBindingCount < 128, "too many concurrent buffer update");
NazaraAssert(imageBindingCount < 128, "too many concurrent image binding update");
NazaraAssert(bindingCount < 128, "too many binding update");
StackVector<VkDescriptorBufferInfo> bufferBinding = NazaraStackVector(VkDescriptorBufferInfo, bufferBindingCount);
StackVector<VkDescriptorImageInfo> imageBinding = NazaraStackVector(VkDescriptorImageInfo, imageBindingCount);
StackVector<VkWriteDescriptorSet> writeOps = NazaraStackVector(VkWriteDescriptorSet, bindingCount);
for (std::size_t i = 0; i < bindingCount; ++i)
@@ -34,7 +60,7 @@ namespace Nz
if constexpr (std::is_same_v<T, StorageBufferBinding>)
{
VulkanBuffer* vkBuffer = static_cast<VulkanBuffer*>(arg.buffer);
VulkanBuffer* vkBuffer = SafeCast<VulkanBuffer*>(arg.buffer);
VkDescriptorBufferInfo& bufferInfo = bufferBinding.emplace_back();
bufferInfo.buffer = (vkBuffer) ? vkBuffer->GetBuffer() : VK_NULL_HANDLE;
@@ -47,8 +73,8 @@ namespace Nz
}
else if constexpr (std::is_same_v<T, TextureBinding>)
{
const VulkanTexture* vkTexture = static_cast<const VulkanTexture*>(arg.texture);
const VulkanTextureSampler* vkSampler = static_cast<const VulkanTextureSampler*>(arg.sampler);
const VulkanTexture* vkTexture = SafeCast<const VulkanTexture*>(arg.texture);
const VulkanTextureSampler* vkSampler = SafeCast<const VulkanTextureSampler*>(arg.sampler);
VkDescriptorImageInfo& imageInfo = imageBinding.emplace_back();
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
@@ -59,6 +85,23 @@ namespace Nz
writeOp.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeOp.pImageInfo = &imageInfo;
}
else if constexpr (std::is_same_v<T, TextureBindings>)
{
for (UInt32 i = 0; i < arg.arraySize; ++i)
{
const VulkanTexture* vkTexture = SafeCast<const VulkanTexture*>(arg.textureBindings[i].texture);
const VulkanTextureSampler* vkSampler = SafeCast<const VulkanTextureSampler*>(arg.textureBindings[i].sampler);
VkDescriptorImageInfo& imageInfo = imageBinding.emplace_back();
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageInfo.imageView = (vkTexture) ? vkTexture->GetImageView() : VK_NULL_HANDLE;
imageInfo.sampler = (vkSampler) ? vkSampler->GetSampler() : VK_NULL_HANDLE;
}
writeOp.descriptorCount = arg.arraySize;
writeOp.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeOp.pImageInfo = &imageBinding[imageBinding.size() - arg.arraySize];
}
else if constexpr (std::is_same_v<T, UniformBufferBinding>)
{
VulkanBuffer* vkBuffer = static_cast<VulkanBuffer*>(arg.buffer);

View File

@@ -293,15 +293,19 @@ namespace Nz
if (!copyCommandBuffer->Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT))
return false;
VkImageAspectFlagBits aspect = VK_IMAGE_ASPECT_COLOR_BIT;
if (PixelFormatInfo::GetContent(m_params.pixelFormat) == PixelFormatContent::Depth)
aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
VkImageSubresourceLayers subresourceLayers = { //< FIXME
VK_IMAGE_ASPECT_COLOR_BIT,
aspect,
level, //< mipLevel
0, //< baseArrayLayer
UInt32((m_params.type == ImageType::Cubemap) ? 6 : 1) //< layerCount
};
VkImageSubresourceRange subresourceRange = { //< FIXME
VK_IMAGE_ASPECT_COLOR_BIT,
aspect,
0, //< baseMipLevel
1, //< levelCount
subresourceLayers.baseArrayLayer, //< baseArrayLayer

View File

@@ -22,6 +22,8 @@ namespace Nz
createInfo.addressModeW = ToVulkan(samplerInfo.wrapModeW);
createInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
createInfo.mipmapMode = ToVulkan(samplerInfo.mipmapMode);
createInfo.compareEnable = samplerInfo.depthCompare;
createInfo.compareOp = ToVulkan(samplerInfo.depthComparison);
if (samplerInfo.anisotropyLevel > 1.f)
{