Add initial support for compute pipelines

This commit is contained in:
SirLynix
2022-12-24 11:54:55 +01:00
committed by Jérôme Leclercq
parent e4064997d8
commit 9578ba3ef5
57 changed files with 915 additions and 182 deletions

View File

@@ -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);

View File

@@ -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);

View 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);
}
}

View File

@@ -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:

View File

@@ -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));
}
}
}

View File

@@ -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());

View File

@@ -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))
{

View File

@@ -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");