Add initial support for compute pipelines
This commit is contained in:
committed by
Jérôme Leclercq
parent
e4064997d8
commit
9578ba3ef5
@@ -402,7 +402,7 @@ namespace Nz
|
||||
viewerData.blitShaderBinding->Update({
|
||||
{
|
||||
0,
|
||||
ShaderBinding::TextureBinding {
|
||||
ShaderBinding::SampledTextureBinding {
|
||||
m_bakedFrameGraph.GetAttachmentTexture(viewerData.debugColorAttachment).get(),
|
||||
sampler.get()
|
||||
}
|
||||
@@ -419,7 +419,7 @@ namespace Nz
|
||||
renderTargetData.blitShaderBinding->Update({
|
||||
{
|
||||
0,
|
||||
ShaderBinding::TextureBinding {
|
||||
ShaderBinding::SampledTextureBinding {
|
||||
m_bakedFrameGraph.GetAttachmentTexture(renderTargetData.finalAttachment).get(),
|
||||
sampler.get()
|
||||
}
|
||||
@@ -456,7 +456,7 @@ namespace Nz
|
||||
{
|
||||
builder.SetScissor(renderRegion);
|
||||
builder.SetViewport(renderRegion);
|
||||
builder.BindPipeline(*graphics->GetBlitPipeline(false));
|
||||
builder.BindRenderPipeline(*graphics->GetBlitPipeline(false));
|
||||
|
||||
builder.BindShaderBinding(0, *data.blitShaderBinding);
|
||||
builder.Draw(3);
|
||||
@@ -644,7 +644,7 @@ namespace Nz
|
||||
builder.SetViewport(env.renderRect);
|
||||
|
||||
Graphics* graphics = Graphics::Instance();
|
||||
builder.BindPipeline(*graphics->GetBlitPipeline(false));
|
||||
builder.BindRenderPipeline(*graphics->GetBlitPipeline(false));
|
||||
|
||||
bool first = true;
|
||||
|
||||
@@ -657,7 +657,7 @@ namespace Nz
|
||||
|
||||
if (first)
|
||||
{
|
||||
builder.BindPipeline(*graphics->GetBlitPipeline(true));
|
||||
builder.BindRenderPipeline(*graphics->GetBlitPipeline(true));
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,9 +108,14 @@ namespace Nz
|
||||
|
||||
RenderDeviceFeatures enabledFeatures;
|
||||
enabledFeatures.anisotropicFiltering = !config.forceDisableFeatures.anisotropicFiltering && renderDeviceInfo[bestRenderDeviceIndex].features.anisotropicFiltering;
|
||||
enabledFeatures.computeShaders = !config.forceDisableFeatures.computeShaders && renderDeviceInfo[bestRenderDeviceIndex].features.computeShaders;
|
||||
enabledFeatures.depthClamping = !config.forceDisableFeatures.depthClamping && renderDeviceInfo[bestRenderDeviceIndex].features.depthClamping;
|
||||
enabledFeatures.nonSolidFaceFilling = !config.forceDisableFeatures.nonSolidFaceFilling && renderDeviceInfo[bestRenderDeviceIndex].features.nonSolidFaceFilling;
|
||||
enabledFeatures.storageBuffers = !config.forceDisableFeatures.storageBuffers && renderDeviceInfo[bestRenderDeviceIndex].features.storageBuffers;
|
||||
enabledFeatures.textureRead = !config.forceDisableFeatures.textureRead && renderDeviceInfo[bestRenderDeviceIndex].features.textureRead;
|
||||
enabledFeatures.textureReadWithoutFormat = !config.forceDisableFeatures.textureReadWithoutFormat && renderDeviceInfo[bestRenderDeviceIndex].features.textureReadWithoutFormat;
|
||||
enabledFeatures.textureWrite = !config.forceDisableFeatures.textureWrite && renderDeviceInfo[bestRenderDeviceIndex].features.textureWrite;
|
||||
enabledFeatures.textureWriteWithoutFormat = !config.forceDisableFeatures.textureWriteWithoutFormat && renderDeviceInfo[bestRenderDeviceIndex].features.textureWriteWithoutFormat;
|
||||
enabledFeatures.unrestrictedTextureViews = !config.forceDisableFeatures.unrestrictedTextureViews && renderDeviceInfo[bestRenderDeviceIndex].features.unrestrictedTextureViews;
|
||||
|
||||
m_renderDevice = renderer->InstanciateRenderDevice(bestRenderDeviceIndex, enabledFeatures);
|
||||
@@ -176,7 +181,7 @@ namespace Nz
|
||||
layoutInfo.bindings.assign({
|
||||
{
|
||||
0, 0, 1,
|
||||
ShaderBindingType::Texture,
|
||||
ShaderBindingType::Sampler,
|
||||
nzsl::ShaderStageType::Fragment
|
||||
}
|
||||
});
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace Nz
|
||||
|
||||
bindings.push_back({
|
||||
textureSlot.bindingIndex,
|
||||
ShaderBinding::TextureBinding {
|
||||
ShaderBinding::SampledTextureBinding {
|
||||
texture.get(), sampler.get()
|
||||
}
|
||||
});
|
||||
|
||||
@@ -56,6 +56,8 @@ namespace Nz
|
||||
if (IsStorageType(*varType))
|
||||
bindingType = ShaderBindingType::StorageBuffer;
|
||||
else if (IsSamplerType(*varType))
|
||||
bindingType = ShaderBindingType::Sampler;
|
||||
else if (IsTextureType(*varType))
|
||||
bindingType = ShaderBindingType::Texture;
|
||||
else if (IsUniformType(*varType))
|
||||
bindingType = ShaderBindingType::UniformBuffer;
|
||||
@@ -67,7 +69,7 @@ namespace Nz
|
||||
throw std::runtime_error("unexpected type " + ToString(innerType) + " in array " + ToString(arrayType));
|
||||
|
||||
arraySize = arrayType.length;
|
||||
bindingType = ShaderBindingType::Texture;
|
||||
bindingType = ShaderBindingType::Sampler;
|
||||
varType = &innerType;
|
||||
}
|
||||
else
|
||||
@@ -86,6 +88,21 @@ namespace Nz
|
||||
{
|
||||
switch (bindingType)
|
||||
{
|
||||
case ShaderBindingType::Sampler:
|
||||
{
|
||||
if (externalBlock->samplers.find(externalVar.tag) != externalBlock->samplers.end())
|
||||
throw std::runtime_error("duplicate sampler tag " + externalVar.tag + " in external block " + node.tag);
|
||||
|
||||
const auto& samplerType = std::get<nzsl::Ast::SamplerType>(*varType);
|
||||
|
||||
ExternalSampler& texture = externalBlock->samplers[externalVar.tag];
|
||||
texture.bindingIndex = bindingIndex;
|
||||
texture.bindingSet = bindingSet;
|
||||
texture.imageType = samplerType.dim;
|
||||
texture.sampledType = samplerType.sampledType;
|
||||
break;
|
||||
}
|
||||
|
||||
case ShaderBindingType::StorageBuffer:
|
||||
{
|
||||
if (externalBlock->storageBlocks.find(externalVar.tag) != externalBlock->storageBlocks.end())
|
||||
@@ -100,16 +117,18 @@ namespace Nz
|
||||
|
||||
case ShaderBindingType::Texture:
|
||||
{
|
||||
if (externalBlock->samplers.find(externalVar.tag) != externalBlock->samplers.end())
|
||||
if (externalBlock->textures.find(externalVar.tag) != externalBlock->textures.end())
|
||||
throw std::runtime_error("duplicate textures tag " + externalVar.tag + " in external block " + node.tag);
|
||||
|
||||
const auto& samplerType = std::get<nzsl::Ast::SamplerType>(*varType);
|
||||
const auto& textureType = std::get<nzsl::Ast::TextureType>(*varType);
|
||||
|
||||
ExternalTexture& texture = externalBlock->samplers[externalVar.tag];
|
||||
ExternalTexture& texture = externalBlock->textures[externalVar.tag];
|
||||
texture.bindingIndex = bindingIndex;
|
||||
texture.bindingSet = bindingSet;
|
||||
texture.imageType = samplerType.dim;
|
||||
texture.sampledType = samplerType.sampledType;
|
||||
texture.bindingSet = bindingSet;
|
||||
texture.accessPolicy = textureType.accessPolicy;
|
||||
texture.baseType = textureType.baseType;
|
||||
texture.imageFormat = textureType.format;
|
||||
texture.imageType = textureType.dim;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ namespace Nz
|
||||
{
|
||||
auto& bindingEntry = m_bindingCache.emplace_back();
|
||||
bindingEntry.bindingIndex = bindingIndex;
|
||||
bindingEntry.content = ShaderBinding::TextureBinding{
|
||||
bindingEntry.content = ShaderBinding::SampledTextureBinding{
|
||||
m_pendingData.currentTextureOverlay, defaultSampler.get()
|
||||
};
|
||||
}
|
||||
@@ -310,7 +310,7 @@ namespace Nz
|
||||
|
||||
if (currentPipeline != drawData.renderPipeline)
|
||||
{
|
||||
commandBuffer.BindPipeline(*drawData.renderPipeline);
|
||||
commandBuffer.BindRenderPipeline(*drawData.renderPipeline);
|
||||
currentPipeline = drawData.renderPipeline;
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ namespace Nz
|
||||
|
||||
auto& bindingEntry = m_bindingCache.emplace_back();
|
||||
bindingEntry.bindingIndex = bindingIndex;
|
||||
bindingEntry.content = ShaderBinding::TextureBindings {
|
||||
bindingEntry.content = ShaderBinding::SampledTextureBindings {
|
||||
SafeCast<UInt32>(renderState.shadowMaps2D.size()), &m_textureBindingCache[textureBindingBaseIndex]
|
||||
};
|
||||
}
|
||||
@@ -197,7 +197,7 @@ namespace Nz
|
||||
|
||||
auto& bindingEntry = m_bindingCache.emplace_back();
|
||||
bindingEntry.bindingIndex = bindingIndex;
|
||||
bindingEntry.content = ShaderBinding::TextureBindings {
|
||||
bindingEntry.content = ShaderBinding::SampledTextureBindings {
|
||||
SafeCast<UInt32>(renderState.shadowMapsCube.size()), &m_textureBindingCache[textureBindingBaseIndex]
|
||||
};
|
||||
}
|
||||
@@ -230,7 +230,7 @@ namespace Nz
|
||||
{
|
||||
auto& bindingEntry = m_bindingCache.emplace_back();
|
||||
bindingEntry.bindingIndex = bindingIndex;
|
||||
bindingEntry.content = ShaderBinding::TextureBinding{
|
||||
bindingEntry.content = ShaderBinding::SampledTextureBinding{
|
||||
whiteTexture2D.get(), defaultSampler.get()
|
||||
};
|
||||
}
|
||||
@@ -285,7 +285,7 @@ namespace Nz
|
||||
|
||||
if (currentPipeline != drawData.renderPipeline)
|
||||
{
|
||||
commandBuffer.BindPipeline(*drawData.renderPipeline);
|
||||
commandBuffer.BindRenderPipeline(*drawData.renderPipeline);
|
||||
currentPipeline = drawData.renderPipeline;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLCommandPool.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLComputePipeline.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLFboFramebuffer.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLRenderPass.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp>
|
||||
@@ -93,6 +94,14 @@ namespace Nz
|
||||
{
|
||||
context->CopyTexture(*command.source, *command.target, command.sourceBox, command.targetPoint);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, DispatchData>)
|
||||
{
|
||||
if (!context->glDispatchCompute)
|
||||
throw std::runtime_error("compute shaders are not supported on this device");
|
||||
|
||||
command.states.pipeline->Apply(*context);
|
||||
context->glDispatchCompute(command.numGroupsX, command.numGroupsY, command.numGroupsZ);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, DrawData>)
|
||||
{
|
||||
ApplyStates(*context, command.states);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLComputePipeline.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLRenderPass.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLRenderPipeline.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp>
|
||||
@@ -26,6 +27,13 @@ namespace Nz
|
||||
m_commandBuffer.SetFramebuffer(static_cast<const OpenGLFramebuffer&>(framebuffer), static_cast<const OpenGLRenderPass&>(renderPass), clearValues, clearValueCount);
|
||||
}
|
||||
|
||||
void OpenGLCommandBufferBuilder::BindComputePipeline(const ComputePipeline& pipeline)
|
||||
{
|
||||
const OpenGLComputePipeline& glPipeline = static_cast<const OpenGLComputePipeline&>(pipeline);
|
||||
|
||||
m_commandBuffer.BindComputePipeline(&glPipeline);
|
||||
}
|
||||
|
||||
void OpenGLCommandBufferBuilder::BindIndexBuffer(const RenderBuffer& indexBuffer, IndexType indexType, UInt64 offset)
|
||||
{
|
||||
const OpenGLBuffer& glBuffer = static_cast<const OpenGLBuffer&>(indexBuffer);
|
||||
@@ -33,11 +41,11 @@ namespace Nz
|
||||
m_commandBuffer.BindIndexBuffer(glBuffer.GetBuffer().GetObjectId(), indexType, offset);
|
||||
}
|
||||
|
||||
void OpenGLCommandBufferBuilder::BindPipeline(const RenderPipeline& pipeline)
|
||||
void OpenGLCommandBufferBuilder::BindRenderPipeline(const RenderPipeline& pipeline)
|
||||
{
|
||||
const OpenGLRenderPipeline& glPipeline = static_cast<const OpenGLRenderPipeline&>(pipeline);
|
||||
|
||||
m_commandBuffer.BindPipeline(&glPipeline);
|
||||
m_commandBuffer.BindRenderPipeline(&glPipeline);
|
||||
}
|
||||
|
||||
void OpenGLCommandBufferBuilder::BindShaderBinding(UInt32 set, const ShaderBinding& binding)
|
||||
@@ -93,6 +101,11 @@ namespace Nz
|
||||
m_commandBuffer.CopyTexture(sourceTexture, fromBox, targetTexture, toPos);
|
||||
}
|
||||
|
||||
void OpenGLCommandBufferBuilder::Dispatch(UInt32 workgroupX, UInt32 workgroupY, UInt32 workgroupZ)
|
||||
{
|
||||
m_commandBuffer.Dispatch(workgroupX, workgroupY, workgroupZ);
|
||||
}
|
||||
|
||||
void OpenGLCommandBufferBuilder::Draw(UInt32 vertexCount, UInt32 instanceCount, UInt32 firstVertex, UInt32 firstInstance)
|
||||
{
|
||||
m_commandBuffer.Draw(vertexCount, instanceCount, firstVertex, firstInstance);
|
||||
|
||||
75
src/Nazara/OpenGLRenderer/OpenGLComputePipeline.cpp
Normal file
75
src/Nazara/OpenGLRenderer/OpenGLComputePipeline.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - OpenGL renderer"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/OpenGLRenderer/OpenGLComputePipeline.hpp>
|
||||
#include <Nazara/Core/ErrorFlags.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLShaderModule.hpp>
|
||||
#include <Nazara/OpenGLRenderer/Utils.hpp>
|
||||
#include <NZSL/GlslWriter.hpp>
|
||||
#include <NZSL/ShaderBuilder.hpp>
|
||||
#include <NZSL/Ast/Module.hpp>
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#include <Nazara/OpenGLRenderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
OpenGLComputePipeline::OpenGLComputePipeline(OpenGLDevice& device, ComputePipelineInfo pipelineInfo) :
|
||||
m_pipelineInfo(std::move(pipelineInfo))
|
||||
{
|
||||
if (device.GetEnabledFeatures().computeShaders)
|
||||
throw std::runtime_error("compute shaders are not enabled on the device");
|
||||
|
||||
OpenGLRenderPipelineLayout& pipelineLayout = static_cast<OpenGLRenderPipelineLayout&>(*m_pipelineInfo.pipelineLayout);
|
||||
|
||||
if (!m_program.Create(device))
|
||||
throw std::runtime_error("failed to create program");
|
||||
|
||||
NazaraAssert(pipelineInfo.shaderModule, "invalid shader module");
|
||||
|
||||
OpenGLShaderModule& shaderModule = static_cast<OpenGLShaderModule&>(*pipelineInfo.shaderModule);
|
||||
|
||||
std::vector<OpenGLShaderModule::ExplicitBinding> explicitBindings;
|
||||
nzsl::ShaderStageTypeFlags stageFlags = shaderModule.Attach(m_program, pipelineLayout.GetBindingMapping(), &explicitBindings);
|
||||
if (!stageFlags.Test(nzsl::ShaderStageType::Compute))
|
||||
throw std::runtime_error("shader module has no compute stage");
|
||||
|
||||
m_program.Link();
|
||||
|
||||
std::string errLog;
|
||||
if (!m_program.GetLinkStatus(&errLog))
|
||||
throw std::runtime_error("failed to link program: " + errLog);
|
||||
|
||||
for (const auto& explicitBinding : explicitBindings)
|
||||
{
|
||||
if (explicitBinding.isBlock)
|
||||
{
|
||||
GLuint blockIndex = m_program.GetUniformBlockIndex(explicitBinding.name);
|
||||
if (blockIndex == GL_INVALID_INDEX)
|
||||
continue;
|
||||
|
||||
m_program.UniformBlockBinding(blockIndex, explicitBinding.binding);
|
||||
}
|
||||
else
|
||||
{
|
||||
int location = m_program.GetUniformLocation(explicitBinding.name);
|
||||
if (location == -1)
|
||||
continue;
|
||||
|
||||
m_program.Uniform(location, SafeCast<int>(explicitBinding.binding));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLComputePipeline::Apply(const GL::Context& context) const
|
||||
{
|
||||
context.BindProgram(m_program.GetObjectId());
|
||||
}
|
||||
|
||||
void OpenGLComputePipeline::UpdateDebugName(std::string_view name)
|
||||
{
|
||||
m_program.SetDebugName(name);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <Nazara/OpenGLRenderer/OpenGLDevice.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLBuffer.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLCommandPool.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLComputePipeline.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLFboFramebuffer.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLRenderPass.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLRenderPipeline.hpp>
|
||||
@@ -62,47 +63,58 @@ namespace Nz
|
||||
if (m_referenceContext->IsExtensionSupported(GL::Extension::TextureFilterAnisotropic))
|
||||
m_deviceInfo.features.anisotropicFiltering = true;
|
||||
|
||||
if (m_referenceContext->IsExtensionSupported(GL::Extension::ComputeShader))
|
||||
m_deviceInfo.features.computeShaders = true;
|
||||
|
||||
if (m_referenceContext->IsExtensionSupported(GL::Extension::DepthClamp))
|
||||
m_deviceInfo.features.depthClamping = true;
|
||||
|
||||
if (m_referenceContext->glPolygonMode) //< not supported in core OpenGL ES, but supported in OpenGL or with GL_NV_polygon_mode extension
|
||||
m_deviceInfo.features.nonSolidFaceFilling = true;
|
||||
|
||||
if (m_referenceContext->IsExtensionSupported(GL::Extension::StorageBuffers))
|
||||
m_deviceInfo.features.storageBuffers = true;
|
||||
|
||||
if (m_referenceContext->glPolygonMode) //< not supported in core OpenGL ES, but supported in OpenGL or with GL_NV_polygon_mode extension
|
||||
m_deviceInfo.features.nonSolidFaceFilling = true;
|
||||
if (m_referenceContext->IsExtensionSupported(GL::Extension::ShaderImageLoadStore))
|
||||
{
|
||||
m_deviceInfo.features.textureRead = true;
|
||||
m_deviceInfo.features.textureWrite = true;
|
||||
m_deviceInfo.features.textureWriteWithoutFormat = true;
|
||||
}
|
||||
|
||||
if (m_referenceContext->IsExtensionSupported(GL::Extension::ShaderImageLoadFormatted))
|
||||
m_deviceInfo.features.textureReadWithoutFormat = true;
|
||||
|
||||
if (m_referenceContext->IsExtensionSupported(GL::Extension::TextureView))
|
||||
m_deviceInfo.features.unrestrictedTextureViews = true;
|
||||
|
||||
// Limits
|
||||
GLint minUboOffsetAlignment;
|
||||
m_referenceContext->glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &minUboOffsetAlignment);
|
||||
|
||||
assert(minUboOffsetAlignment >= 1);
|
||||
m_deviceInfo.limits.minUniformBufferOffsetAlignment = static_cast<UInt64>(minUboOffsetAlignment);
|
||||
|
||||
GLint maxUboBlockSize;
|
||||
m_referenceContext->glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUboBlockSize);
|
||||
|
||||
assert(maxUboBlockSize >= 1);
|
||||
m_deviceInfo.limits.maxUniformBufferSize = static_cast<UInt64>(maxUboBlockSize);
|
||||
m_deviceInfo.limits.maxUniformBufferSize = m_referenceContext->GetInteger<UInt64>(GL_MAX_UNIFORM_BLOCK_SIZE);
|
||||
m_deviceInfo.limits.minUniformBufferOffsetAlignment = m_referenceContext->GetInteger<UInt64>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
|
||||
|
||||
if (m_deviceInfo.features.storageBuffers)
|
||||
{
|
||||
GLint minStorageOffsetAlignment;
|
||||
m_referenceContext->glGetIntegerv(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, &minStorageOffsetAlignment);
|
||||
|
||||
assert(minStorageOffsetAlignment >= 1);
|
||||
m_deviceInfo.limits.minStorageBufferOffsetAlignment = static_cast<UInt64>(minStorageOffsetAlignment);
|
||||
|
||||
GLint maxStorageBlockSize;
|
||||
m_referenceContext->glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &maxStorageBlockSize);
|
||||
|
||||
assert(maxStorageBlockSize >= 1);
|
||||
m_deviceInfo.limits.maxStorageBufferSize = static_cast<UInt64>(maxStorageBlockSize);
|
||||
m_deviceInfo.limits.maxStorageBufferSize = m_referenceContext->GetInteger<UInt64>(GL_MAX_SHADER_STORAGE_BLOCK_SIZE);
|
||||
m_deviceInfo.limits.minStorageBufferOffsetAlignment = m_referenceContext->GetInteger<UInt64>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT);
|
||||
}
|
||||
|
||||
if (m_deviceInfo.features.computeShaders)
|
||||
{
|
||||
m_deviceInfo.limits.maxComputeSharedMemorySize = m_referenceContext->GetInteger<UInt64>(GL_MAX_COMPUTE_SHARED_MEMORY_SIZE);
|
||||
m_deviceInfo.limits.maxComputeWorkGroupInvocations = m_referenceContext->GetInteger<UInt32>(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS);
|
||||
|
||||
m_deviceInfo.limits.maxComputeWorkGroupCount = {
|
||||
m_referenceContext->GetInteger<UInt32>(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0),
|
||||
m_referenceContext->GetInteger<UInt32>(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1),
|
||||
m_referenceContext->GetInteger<UInt32>(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2)
|
||||
};
|
||||
|
||||
m_deviceInfo.limits.maxComputeWorkGroupSize = {
|
||||
m_referenceContext->GetInteger<UInt32>(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0),
|
||||
m_referenceContext->GetInteger<UInt32>(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1),
|
||||
m_referenceContext->GetInteger<UInt32>(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2)
|
||||
};
|
||||
}
|
||||
else
|
||||
m_deviceInfo.limits.maxStorageBufferSize = 0;
|
||||
|
||||
m_contexts.insert(m_referenceContext.get());
|
||||
}
|
||||
@@ -154,6 +166,11 @@ namespace Nz
|
||||
return std::make_shared<OpenGLCommandPool>();
|
||||
}
|
||||
|
||||
std::shared_ptr<ComputePipeline> OpenGLDevice::InstantiateComputePipeline(ComputePipelineInfo pipelineInfo)
|
||||
{
|
||||
return std::make_shared<OpenGLComputePipeline>(*this, std::move(pipelineInfo));
|
||||
}
|
||||
|
||||
std::shared_ptr<Framebuffer> OpenGLDevice::InstantiateFramebuffer(unsigned int /*width*/, unsigned int /*height*/, const std::shared_ptr<RenderPass>& /*renderPass*/, const std::vector<std::shared_ptr<Texture>>& attachments)
|
||||
{
|
||||
return std::make_shared<OpenGLFboFramebuffer>(*this, attachments);
|
||||
@@ -246,7 +263,7 @@ namespace Nz
|
||||
case PixelFormat::RGBA32F:
|
||||
case PixelFormat::RGBA32I:
|
||||
case PixelFormat::RGBA32UI:
|
||||
return usage == TextureUsage::ColorAttachment || usage == TextureUsage::InputAttachment || usage == TextureUsage::ShaderSampling || usage == TextureUsage::TransferDestination || usage == TextureUsage::TransferSource;
|
||||
return usage == TextureUsage::ColorAttachment || usage == TextureUsage::InputAttachment || usage == TextureUsage::ShaderSampling || usage == TextureUsage::ShaderReadWrite || usage == TextureUsage::TransferDestination || usage == TextureUsage::TransferSource;
|
||||
|
||||
case PixelFormat::DXT1:
|
||||
case PixelFormat::DXT3:
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace Nz
|
||||
if (location == -1)
|
||||
continue;
|
||||
|
||||
m_program.Uniform(location, static_cast<int>(explicitBinding.binding)); //< FIXME: Use SafeCast
|
||||
m_program.Uniform(location, SafeCast<int>(explicitBinding.binding));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,16 @@ namespace Nz
|
||||
return ShaderBindingPtr(PlacementNew(freeBindingMemory, *this, poolIndex, freeBindingId));
|
||||
}
|
||||
|
||||
auto OpenGLRenderPipelineLayout::GetSampledTextureDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex) -> SampledTextureDescriptor&
|
||||
{
|
||||
assert(poolIndex < m_descriptorPools.size());
|
||||
auto& pool = m_descriptorPools[poolIndex];
|
||||
assert(!pool.freeBindings.Test(bindingIndex));
|
||||
assert(descriptorIndex < m_maxDescriptorCount);
|
||||
|
||||
return pool.descriptors[bindingIndex * m_maxDescriptorCount + descriptorIndex].emplace<SampledTextureDescriptor>();
|
||||
}
|
||||
|
||||
auto OpenGLRenderPipelineLayout::GetStorageBufferDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex) -> StorageBufferDescriptor&
|
||||
{
|
||||
assert(poolIndex < m_descriptorPools.size());
|
||||
|
||||
@@ -45,7 +45,15 @@ namespace Nz
|
||||
|
||||
UInt32 bindingPoint = bindingMappingIt->second;
|
||||
|
||||
if constexpr (std::is_same_v<DescriptorType, OpenGLRenderPipelineLayout::StorageBufferDescriptor>)
|
||||
if constexpr (std::is_same_v<DescriptorType, OpenGLRenderPipelineLayout::SampledTextureDescriptor>)
|
||||
{
|
||||
if (bindingInfo.type != ShaderBindingType::Sampler)
|
||||
throw std::runtime_error("descriptor (set=" + std::to_string(setIndex) + ", binding=" + std::to_string(bindingIndex) + ") is not a sampler");
|
||||
|
||||
context.BindSampler(bindingPoint, descriptor.sampler);
|
||||
context.BindTexture(bindingPoint, descriptor.textureTarget, descriptor.texture);
|
||||
}
|
||||
else if constexpr (std::is_same_v<DescriptorType, OpenGLRenderPipelineLayout::StorageBufferDescriptor>)
|
||||
{
|
||||
if (bindingInfo.type != ShaderBindingType::StorageBuffer)
|
||||
throw std::runtime_error("descriptor (set=" + std::to_string(setIndex) + ", binding=" + std::to_string(bindingIndex) + ") is not a storage buffer");
|
||||
@@ -57,8 +65,7 @@ namespace Nz
|
||||
if (bindingInfo.type != ShaderBindingType::Texture)
|
||||
throw std::runtime_error("descriptor (set=" + std::to_string(setIndex) + ", binding=" + std::to_string(bindingIndex) + ") is not a texture");
|
||||
|
||||
context.BindSampler(bindingPoint, descriptor.sampler);
|
||||
context.BindTexture(bindingPoint, descriptor.textureTarget, descriptor.texture);
|
||||
context.BindImageTexture(bindingPoint, descriptor.texture, descriptor.level, descriptor.layered, descriptor.layer, descriptor.access, descriptor.format);
|
||||
}
|
||||
else if constexpr (std::is_same_v<DescriptorType, OpenGLRenderPipelineLayout::UniformBufferDescriptor>)
|
||||
{
|
||||
@@ -82,7 +89,14 @@ namespace Nz
|
||||
{
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, StorageBufferBinding>)
|
||||
if constexpr (std::is_same_v<T, SampledTextureBinding>)
|
||||
HandleTextureBinding(binding.bindingIndex, arg);
|
||||
else if constexpr (std::is_same_v<T, SampledTextureBindings>)
|
||||
{
|
||||
for (UInt32 i = 0; i < arg.arraySize; ++i)
|
||||
HandleTextureBinding(binding.bindingIndex + i, arg.textureBindings[i]);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, StorageBufferBinding>)
|
||||
{
|
||||
auto& storageDescriptor = m_owner.GetStorageBufferDescriptor(m_poolIndex, m_bindingIndex, binding.bindingIndex);
|
||||
storageDescriptor.offset = arg.offset;
|
||||
@@ -99,11 +113,41 @@ 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>)
|
||||
{
|
||||
for (UInt32 i = 0; i < arg.arraySize; ++i)
|
||||
HandleTextureBinding(binding.bindingIndex + i, arg.textureBindings[i]);
|
||||
auto& textureDescriptor = m_owner.GetTextureDescriptor(m_poolIndex, m_bindingIndex, binding.bindingIndex);
|
||||
if (const OpenGLTexture* glTexture = static_cast<const OpenGLTexture*>(arg.texture))
|
||||
{
|
||||
const TextureViewInfo& viewInfo = glTexture->GetTextureViewInfo();
|
||||
|
||||
std::optional<GLTextureFormat> format = DescribeTextureFormat(viewInfo.reinterpretFormat);
|
||||
if (!format)
|
||||
throw std::runtime_error("unexpected texture format");
|
||||
|
||||
textureDescriptor.access = ToOpenGL(arg.access);
|
||||
textureDescriptor.format = format->internalFormat;
|
||||
if (viewInfo.layerCount > 1)
|
||||
{
|
||||
textureDescriptor.layered = true;
|
||||
textureDescriptor.layer = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
textureDescriptor.layered = false;
|
||||
textureDescriptor.layer = viewInfo.baseArrayLayer;
|
||||
}
|
||||
|
||||
textureDescriptor.level = viewInfo.baseMipLevel;
|
||||
textureDescriptor.texture = glTexture->GetTexture().GetObjectId();
|
||||
}
|
||||
else
|
||||
{
|
||||
textureDescriptor.access = GL_READ_ONLY;
|
||||
textureDescriptor.format = GL_RGBA8;
|
||||
textureDescriptor.layer = 0;
|
||||
textureDescriptor.layered = GL_FALSE;
|
||||
textureDescriptor.level = 0;
|
||||
textureDescriptor.texture = 0;
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, UniformBufferBinding>)
|
||||
{
|
||||
@@ -133,9 +177,9 @@ namespace Nz
|
||||
// No OpenGL object to name
|
||||
}
|
||||
|
||||
void OpenGLShaderBinding::HandleTextureBinding(UInt32 bindingIndex, const TextureBinding& textureBinding)
|
||||
void OpenGLShaderBinding::HandleTextureBinding(UInt32 bindingIndex, const SampledTextureBinding& textureBinding)
|
||||
{
|
||||
auto& textureDescriptor = m_owner.GetTextureDescriptor(m_poolIndex, m_bindingIndex, bindingIndex);
|
||||
auto& textureDescriptor = m_owner.GetSampledTextureDescriptor(m_poolIndex, m_bindingIndex, bindingIndex);
|
||||
|
||||
if (const OpenGLTexture* glTexture = static_cast<const OpenGLTexture*>(textureBinding.texture))
|
||||
{
|
||||
|
||||
@@ -172,6 +172,32 @@ namespace Nz::GL
|
||||
}
|
||||
}
|
||||
|
||||
void Context::BindImageTexture(GLuint imageUnit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) const
|
||||
{
|
||||
if (imageUnit >= m_state.imageUnits.size())
|
||||
throw std::runtime_error("unsupported image unit #" + std::to_string(imageUnit));
|
||||
|
||||
layer = (layered == GL_TRUE) ? layer : 0;
|
||||
|
||||
auto& unit = m_state.imageUnits[imageUnit];
|
||||
if (unit.texture != texture || unit.level != level || unit.layered != layered || unit.layer != layer || unit.access != access || unit.format != format)
|
||||
{
|
||||
if (!SetCurrentContext(this))
|
||||
throw std::runtime_error("failed to activate context");
|
||||
|
||||
if (!glBindImageTexture)
|
||||
throw std::runtime_error("image binding is not supported");
|
||||
|
||||
glBindImageTexture(imageUnit, texture, level, layered, layer, access, format);
|
||||
unit.access = access;
|
||||
unit.format = format;
|
||||
unit.layer = layer;
|
||||
unit.layered = layered;
|
||||
unit.level = level;
|
||||
unit.texture = texture;
|
||||
}
|
||||
}
|
||||
|
||||
void Context::BindProgram(GLuint program) const
|
||||
{
|
||||
if (m_state.boundProgram != program)
|
||||
@@ -413,21 +439,13 @@ namespace Nz::GL
|
||||
return false;
|
||||
}
|
||||
|
||||
GLint majorVersion = 0;
|
||||
glGetIntegerv(GL_MAJOR_VERSION, &majorVersion);
|
||||
m_params.glMajorVersion = GetInteger<unsigned int>(GL_MAJOR_VERSION);
|
||||
m_params.glMinorVersion = GetInteger<unsigned int>(GL_MINOR_VERSION);
|
||||
|
||||
GLint minorVersion = 0;
|
||||
glGetIntegerv(GL_MINOR_VERSION, &minorVersion);
|
||||
|
||||
m_params.glMajorVersion = majorVersion;
|
||||
m_params.glMinorVersion = minorVersion;
|
||||
|
||||
unsigned int glVersion = majorVersion * 100 + minorVersion * 10;
|
||||
unsigned int glVersion = m_params.glMajorVersion * 100 + m_params.glMinorVersion * 10;
|
||||
|
||||
// Load extensions
|
||||
GLint extensionCount = 0;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount);
|
||||
|
||||
GLint extensionCount = GetInteger<GLint>(GL_NUM_EXTENSIONS);
|
||||
for (GLint i = 0; i < extensionCount; ++i)
|
||||
m_supportedExtensions.emplace(reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i)));
|
||||
|
||||
@@ -441,6 +459,12 @@ namespace Nz::GL
|
||||
else if (m_supportedExtensions.count("GL_EXT_clip_control"))
|
||||
m_extensionStatus[UnderlyingCast(Extension::ClipControl)] = ExtensionStatus::EXT;
|
||||
|
||||
// Compute shaders
|
||||
if ((m_params.type == ContextType::OpenGL && glVersion >= 430) || (m_params.type == ContextType::OpenGL_ES && glVersion >= 310))
|
||||
m_extensionStatus[UnderlyingCast(Extension::ComputeShader)] = ExtensionStatus::Core;
|
||||
else if (m_supportedExtensions.count("GL_ARB_compute_shader"))
|
||||
m_extensionStatus[UnderlyingCast(Extension::ComputeShader)] = ExtensionStatus::ARB;
|
||||
|
||||
// Debug output
|
||||
if ((m_params.type == ContextType::OpenGL && glVersion >= 430) || (m_params.type == ContextType::OpenGL_ES && glVersion >= 320))
|
||||
m_extensionStatus[UnderlyingCast(Extension::DebugOutput)] = ExtensionStatus::Core;
|
||||
@@ -465,6 +489,18 @@ namespace Nz::GL
|
||||
else if (m_supportedExtensions.count("GL_NV_polygon_mode"))
|
||||
m_extensionStatus[UnderlyingCast(Extension::DepthClamp)] = ExtensionStatus::Vendor;
|
||||
|
||||
// Shader image load formatted
|
||||
if (m_supportedExtensions.count("GL_EXT_shader_image_load_formatted"))
|
||||
m_extensionStatus[UnderlyingCast(Extension::ShaderImageLoadFormatted)] = ExtensionStatus::EXT;
|
||||
|
||||
// Shader image load/store
|
||||
if ((m_params.type == ContextType::OpenGL && glVersion >= 420) || (m_params.type == ContextType::OpenGL_ES && glVersion >= 310))
|
||||
m_extensionStatus[UnderlyingCast(Extension::ShaderImageLoadStore)] = ExtensionStatus::Core;
|
||||
else if (m_supportedExtensions.count("GL_ARB_shader_image_load_store"))
|
||||
m_extensionStatus[UnderlyingCast(Extension::ShaderImageLoadStore)] = ExtensionStatus::ARB;
|
||||
else if (m_supportedExtensions.count("GL_EXT_shader_image_load_store"))
|
||||
m_extensionStatus[UnderlyingCast(Extension::ShaderImageLoadStore)] = ExtensionStatus::EXT;
|
||||
|
||||
// SPIR-V support
|
||||
if (m_params.type == ContextType::OpenGL && glVersion >= 460)
|
||||
m_extensionStatus[UnderlyingCast(Extension::SpirV)] = ExtensionStatus::Core;
|
||||
@@ -583,16 +619,14 @@ namespace Nz::GL
|
||||
else
|
||||
m_params.validationLevel = m_params.validationLevel;
|
||||
|
||||
GLint maxTextureUnits = -1;
|
||||
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
|
||||
unsigned int maxTextureUnits = GetInteger<unsigned int>(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS);
|
||||
if (maxTextureUnits < 32) //< OpenGL ES 3.0 requires at least 32 textures units
|
||||
NazaraWarning("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS is " + std::to_string(maxTextureUnits) + ", expected >= 32");
|
||||
|
||||
assert(maxTextureUnits > 0);
|
||||
m_state.textureUnits.resize(maxTextureUnits);
|
||||
|
||||
GLint maxUniformBufferUnits = -1;
|
||||
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferUnits);
|
||||
unsigned int maxUniformBufferUnits = GetInteger<unsigned int>(GL_MAX_UNIFORM_BUFFER_BINDINGS);
|
||||
if (maxUniformBufferUnits < 24) //< OpenGL ES 3.0 requires at least 24 uniform buffers units
|
||||
NazaraWarning("GL_MAX_UNIFORM_BUFFER_BINDINGS is " + std::to_string(maxUniformBufferUnits) + ", expected >= 24");
|
||||
|
||||
@@ -601,8 +635,7 @@ namespace Nz::GL
|
||||
|
||||
if (IsExtensionSupported(Extension::StorageBuffers))
|
||||
{
|
||||
GLint maxStorageBufferUnits = -1;
|
||||
glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &maxStorageBufferUnits);
|
||||
unsigned int maxStorageBufferUnits = GetInteger<unsigned int>(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS);
|
||||
if (maxStorageBufferUnits < 8) //< OpenGL ES 3.1 requires at least 8 storage buffers units
|
||||
NazaraWarning("GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS is " + std::to_string(maxUniformBufferUnits) + ", expected >= 8");
|
||||
|
||||
|
||||
11
src/Nazara/Renderer/ComputePipeline.cpp
Normal file
11
src/Nazara/Renderer/ComputePipeline.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Renderer module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Renderer/ComputePipeline.hpp>
|
||||
#include <Nazara/Renderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
ComputePipeline::~ComputePipeline() = default;
|
||||
}
|
||||
@@ -83,7 +83,7 @@ namespace Nz
|
||||
return;
|
||||
|
||||
builder.BindShaderBinding(0, *m_currentViewerData.binding);
|
||||
builder.BindPipeline(*m_renderPipeline);
|
||||
builder.BindRenderPipeline(*m_renderPipeline);
|
||||
|
||||
for (auto& drawCall : m_drawCalls)
|
||||
{
|
||||
|
||||
@@ -42,9 +42,14 @@ namespace Nz
|
||||
}
|
||||
|
||||
NzValidateFeature(anisotropicFiltering, "anistropic filtering feature")
|
||||
NzValidateFeature(computeShaders, "compute shaders feature")
|
||||
NzValidateFeature(depthClamping, "depth clamping feature")
|
||||
NzValidateFeature(nonSolidFaceFilling, "non-solid face filling feature")
|
||||
NzValidateFeature(storageBuffers, "storage buffers support")
|
||||
NzValidateFeature(textureRead, "texture read")
|
||||
NzValidateFeature(textureReadWithoutFormat, "texture read without format")
|
||||
NzValidateFeature(textureWrite, "texture write")
|
||||
NzValidateFeature(textureWriteWithoutFormat, "texture write without format")
|
||||
NzValidateFeature(unrestrictedTextureViews, "unrestricted texture view support")
|
||||
|
||||
#undef NzValidateFeature
|
||||
|
||||
@@ -243,7 +243,7 @@ namespace Nz
|
||||
|
||||
void Seek(UInt64 frameIndex) override
|
||||
{
|
||||
assert(frameIndex < m_frames.size());
|
||||
assert(frameIndex <= m_frames.size());
|
||||
|
||||
if (m_requiresFrameHistory)
|
||||
{
|
||||
|
||||
@@ -54,11 +54,20 @@ namespace Nz
|
||||
deviceInfo.name = physDevice.properties.deviceName;
|
||||
|
||||
deviceInfo.features.anisotropicFiltering = physDevice.features.samplerAnisotropy;
|
||||
deviceInfo.features.computeShaders = true;
|
||||
deviceInfo.features.depthClamping = physDevice.features.depthClamp;
|
||||
deviceInfo.features.nonSolidFaceFilling = physDevice.features.fillModeNonSolid;
|
||||
deviceInfo.features.storageBuffers = true;
|
||||
deviceInfo.features.textureRead = true;
|
||||
deviceInfo.features.textureReadWithoutFormat = physDevice.features.shaderStorageImageReadWithoutFormat;
|
||||
deviceInfo.features.textureWrite = true;
|
||||
deviceInfo.features.textureWriteWithoutFormat = physDevice.features.shaderStorageImageWriteWithoutFormat;
|
||||
deviceInfo.features.unrestrictedTextureViews = true;
|
||||
|
||||
deviceInfo.limits.maxComputeSharedMemorySize = physDevice.properties.limits.maxComputeSharedMemorySize;
|
||||
deviceInfo.limits.maxComputeWorkGroupCount = { physDevice.properties.limits.maxComputeWorkGroupCount[0], physDevice.properties.limits.maxComputeWorkGroupCount[1], physDevice.properties.limits.maxComputeWorkGroupCount[2] };
|
||||
deviceInfo.limits.maxComputeWorkGroupSize = { physDevice.properties.limits.maxComputeWorkGroupSize[0], physDevice.properties.limits.maxComputeWorkGroupSize[1], physDevice.properties.limits.maxComputeWorkGroupSize[2] };
|
||||
deviceInfo.limits.maxComputeWorkGroupInvocations = physDevice.properties.limits.maxComputeWorkGroupInvocations;
|
||||
deviceInfo.limits.maxStorageBufferSize = physDevice.properties.limits.maxStorageBufferRange;
|
||||
deviceInfo.limits.maxUniformBufferSize = physDevice.properties.limits.maxUniformBufferRange;
|
||||
deviceInfo.limits.minStorageBufferOffsetAlignment = physDevice.properties.limits.minStorageBufferOffsetAlignment;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp>
|
||||
#include <Nazara/Utility/PixelFormat.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanBuffer.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanComputePipeline.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanRenderPass.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanRenderPipeline.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanRenderPipelineLayout.hpp>
|
||||
@@ -72,6 +73,13 @@ namespace Nz
|
||||
m_currentSubpassIndex = 0;
|
||||
}
|
||||
|
||||
void VulkanCommandBufferBuilder::BindComputePipeline(const ComputePipeline& pipeline)
|
||||
{
|
||||
const VulkanComputePipeline& vkPipeline = static_cast<const VulkanComputePipeline&>(pipeline);
|
||||
|
||||
m_commandBuffer.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vkPipeline.GetPipeline());
|
||||
}
|
||||
|
||||
void VulkanCommandBufferBuilder::BindIndexBuffer(const RenderBuffer& indexBuffer, IndexType indexType, UInt64 offset)
|
||||
{
|
||||
const VulkanBuffer& vkBuffer = static_cast<const VulkanBuffer&>(indexBuffer);
|
||||
@@ -79,14 +87,14 @@ namespace Nz
|
||||
m_commandBuffer.BindIndexBuffer(vkBuffer.GetBuffer(), offset, ToVulkan(indexType));
|
||||
}
|
||||
|
||||
void VulkanCommandBufferBuilder::BindPipeline(const RenderPipeline& pipeline)
|
||||
void VulkanCommandBufferBuilder::BindRenderPipeline(const RenderPipeline& pipeline)
|
||||
{
|
||||
if (!m_currentRenderPass)
|
||||
throw std::runtime_error("BindPipeline must be called in a RenderPass");
|
||||
|
||||
const VulkanRenderPipeline& vkBinding = static_cast<const VulkanRenderPipeline&>(pipeline);
|
||||
const VulkanRenderPipeline& vkPipeline = static_cast<const VulkanRenderPipeline&>(pipeline);
|
||||
|
||||
m_commandBuffer.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vkBinding.Get(*m_currentRenderPass, m_currentSubpassIndex));
|
||||
m_commandBuffer.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vkPipeline.Get(*m_currentRenderPass, m_currentSubpassIndex));
|
||||
}
|
||||
|
||||
void VulkanCommandBufferBuilder::BindShaderBinding(UInt32 set, const ShaderBinding& binding)
|
||||
@@ -207,6 +215,11 @@ namespace Nz
|
||||
m_commandBuffer.CopyImage(vkFromTexture.GetImage(), ToVulkan(fromLayout), vkToTexture.GetImage(), ToVulkan(toLayout), region);
|
||||
}
|
||||
|
||||
void VulkanCommandBufferBuilder::Dispatch(UInt32 workgroupX, UInt32 workgroupY, UInt32 workgroupZ)
|
||||
{
|
||||
m_commandBuffer.Dispatch(workgroupX, workgroupY, workgroupZ);
|
||||
}
|
||||
|
||||
void VulkanCommandBufferBuilder::Draw(UInt32 vertexCount, UInt32 instanceCount, UInt32 firstVertex, UInt32 firstInstance)
|
||||
{
|
||||
m_commandBuffer.Draw(vertexCount, instanceCount, firstVertex, firstInstance);
|
||||
|
||||
61
src/Nazara/VulkanRenderer/VulkanComputePipeline.cpp
Normal file
61
src/Nazara/VulkanRenderer/VulkanComputePipeline.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Vulkan renderer"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/VulkanRenderer/VulkanComputePipeline.hpp>
|
||||
#include <Nazara/Core/ErrorFlags.hpp>
|
||||
#include <Nazara/VulkanRenderer/Utils.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanDevice.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanComputePipeline.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanRenderPipelineLayout.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanShaderModule.hpp>
|
||||
#include <cassert>
|
||||
#include <Nazara/VulkanRenderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
VulkanComputePipeline::VulkanComputePipeline(VulkanDevice& device, ComputePipelineInfo pipelineInfo) :
|
||||
m_pipelineInfo(std::move(pipelineInfo))
|
||||
{
|
||||
if (device.GetEnabledFeatures().computeShaders)
|
||||
throw std::runtime_error("compute shaders are not enabled on the device");
|
||||
|
||||
VulkanShaderModule& vulkanModule = static_cast<VulkanShaderModule&>(*m_pipelineInfo.shaderModule);
|
||||
const VulkanShaderModule::Stage* computeStage = nullptr;
|
||||
for (const auto& stage : vulkanModule.GetStages())
|
||||
{
|
||||
if (stage.stage != nzsl::ShaderStageType::Compute)
|
||||
continue;
|
||||
|
||||
if (computeStage)
|
||||
throw std::runtime_error("multiple compute stages found in shader module");
|
||||
|
||||
computeStage = &stage;
|
||||
}
|
||||
|
||||
if (!computeStage)
|
||||
throw std::runtime_error("no compute stages found in shader module");
|
||||
|
||||
VkComputePipelineCreateInfo createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
||||
createInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
createInfo.stage.module = vulkanModule.GetHandle();
|
||||
createInfo.stage.pName = computeStage->name.data();
|
||||
createInfo.stage.stage = ToVulkan(computeStage->stage);
|
||||
|
||||
VulkanRenderPipelineLayout& pipelineLayout = *static_cast<VulkanRenderPipelineLayout*>(m_pipelineInfo.pipelineLayout.get());
|
||||
createInfo.layout = pipelineLayout.GetPipelineLayout();
|
||||
|
||||
if (!m_pipeline.CreateCompute(device, createInfo))
|
||||
throw std::runtime_error("failed to create compute pipeline: " + TranslateVulkanError(m_pipeline.GetLastErrorCode()));
|
||||
}
|
||||
|
||||
void VulkanComputePipeline::UpdateDebugName(std::string_view name)
|
||||
{
|
||||
m_pipeline.SetDebugName(name);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(NAZARA_PLATFORM_WINDOWS)
|
||||
#include <Nazara/Core/AntiWindows.hpp>
|
||||
#endif
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <Nazara/VulkanRenderer/VulkanDevice.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanCommandPool.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanComputePipeline.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanRenderPass.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanRenderPipeline.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanRenderPipelineLayout.hpp>
|
||||
@@ -37,6 +38,11 @@ namespace Nz
|
||||
return std::make_shared<VulkanCommandPool>(*this, queueType);
|
||||
}
|
||||
|
||||
std::shared_ptr<ComputePipeline> VulkanDevice::InstantiateComputePipeline(ComputePipelineInfo pipelineInfo)
|
||||
{
|
||||
return std::make_shared<VulkanComputePipeline>(*this, std::move(pipelineInfo));
|
||||
}
|
||||
|
||||
std::shared_ptr<Framebuffer> VulkanDevice::InstantiateFramebuffer(unsigned int width, unsigned int height, const std::shared_ptr<RenderPass>& renderPass, const std::vector<std::shared_ptr<Texture>>& attachments)
|
||||
{
|
||||
return std::make_shared<VulkanTextureFramebuffer>(*this, width, height, renderPass, attachments);
|
||||
@@ -111,6 +117,10 @@ namespace Nz
|
||||
flags = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
|
||||
break;
|
||||
|
||||
case TextureUsage::ShaderReadWrite:
|
||||
flags = VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT;
|
||||
break;
|
||||
|
||||
case TextureUsage::TransferSource:
|
||||
flags = VK_FORMAT_FEATURE_TRANSFER_SRC_BIT;
|
||||
break;
|
||||
|
||||
@@ -27,9 +27,9 @@ namespace Nz
|
||||
|
||||
if constexpr (std::is_same_v<T, StorageBufferBinding> || std::is_same_v<T, UniformBufferBinding>)
|
||||
bufferBindingCount++;
|
||||
else if constexpr (std::is_same_v<T, TextureBinding>)
|
||||
else if constexpr (std::is_same_v<T, SampledTextureBinding> || std::is_same_v<T, TextureBinding>)
|
||||
imageBindingCount++;
|
||||
else if constexpr (std::is_same_v<T, TextureBindings>)
|
||||
else if constexpr (std::is_same_v<T, SampledTextureBindings>)
|
||||
imageBindingCount += arg.arraySize;
|
||||
else
|
||||
static_assert(AlwaysFalse<T>(), "non-exhaustive visitor");
|
||||
@@ -58,20 +58,7 @@ namespace Nz
|
||||
{
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, StorageBufferBinding>)
|
||||
{
|
||||
VulkanBuffer* vkBuffer = SafeCast<VulkanBuffer*>(arg.buffer);
|
||||
|
||||
VkDescriptorBufferInfo& bufferInfo = bufferBinding.emplace_back();
|
||||
bufferInfo.buffer = (vkBuffer) ? vkBuffer->GetBuffer() : VK_NULL_HANDLE;
|
||||
bufferInfo.offset = arg.offset;
|
||||
bufferInfo.range = arg.range;
|
||||
|
||||
writeOp.descriptorCount = 1;
|
||||
writeOp.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
writeOp.pBufferInfo = &bufferInfo;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, TextureBinding>)
|
||||
if constexpr (std::is_same_v<T, SampledTextureBinding>)
|
||||
{
|
||||
const VulkanTexture* vkTexture = SafeCast<const VulkanTexture*>(arg.texture);
|
||||
const VulkanTextureSampler* vkSampler = SafeCast<const VulkanTextureSampler*>(arg.sampler);
|
||||
@@ -85,7 +72,7 @@ namespace Nz
|
||||
writeOp.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
writeOp.pImageInfo = &imageInfo;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, TextureBindings>)
|
||||
else if constexpr (std::is_same_v<T, SampledTextureBindings>)
|
||||
{
|
||||
for (UInt32 i = 0; i < arg.arraySize; ++i)
|
||||
{
|
||||
@@ -102,6 +89,32 @@ namespace Nz
|
||||
writeOp.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
writeOp.pImageInfo = &imageBinding[imageBinding.size() - arg.arraySize];
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, StorageBufferBinding>)
|
||||
{
|
||||
VulkanBuffer* vkBuffer = SafeCast<VulkanBuffer*>(arg.buffer);
|
||||
|
||||
VkDescriptorBufferInfo& bufferInfo = bufferBinding.emplace_back();
|
||||
bufferInfo.buffer = (vkBuffer) ? vkBuffer->GetBuffer() : VK_NULL_HANDLE;
|
||||
bufferInfo.offset = arg.offset;
|
||||
bufferInfo.range = arg.range;
|
||||
|
||||
writeOp.descriptorCount = 1;
|
||||
writeOp.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
writeOp.pBufferInfo = &bufferInfo;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, TextureBinding>)
|
||||
{
|
||||
const VulkanTexture* vkTexture = SafeCast<const VulkanTexture*>(arg.texture);
|
||||
|
||||
VkDescriptorImageInfo& imageInfo = imageBinding.emplace_back();
|
||||
imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
imageInfo.imageView = (vkTexture) ? vkTexture->GetImageView() : VK_NULL_HANDLE;
|
||||
imageInfo.sampler = VK_NULL_HANDLE;
|
||||
|
||||
writeOp.descriptorCount = 1;
|
||||
writeOp.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
||||
writeOp.pImageInfo = &imageInfo;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, UniformBufferBinding>)
|
||||
{
|
||||
VulkanBuffer* vkBuffer = static_cast<VulkanBuffer*>(arg.buffer);
|
||||
|
||||
Reference in New Issue
Block a user