2026-04-25 08:47:06 -07:00
|
|
|
/* Copyright (C) 2026 Wildfire Games.
|
2023-12-02 16:30:12 -08:00
|
|
|
* This file is part of 0 A.D.
|
2022-01-05 06:49:54 -08:00
|
|
|
*
|
2023-12-02 16:30:12 -08:00
|
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
2022-01-05 06:49:54 -08:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
2023-12-02 16:30:12 -08:00
|
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
2022-01-05 06:49:54 -08:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2023-12-02 16:30:12 -08:00
|
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
2022-01-05 06:49:54 -08:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "precompiled.h"
|
|
|
|
|
|
|
|
|
|
#include "DeviceCommandContext.h"
|
|
|
|
|
|
2025-07-10 12:03:11 -07:00
|
|
|
#include "lib/config2.h"
|
|
|
|
|
#include "lib/debug.h"
|
2022-02-18 09:33:12 -08:00
|
|
|
#include "ps/CLogger.h"
|
2025-07-06 11:15:27 -07:00
|
|
|
#include "renderer/backend/Barrier.h"
|
|
|
|
|
#include "renderer/backend/Format.h"
|
2025-07-10 12:03:11 -07:00
|
|
|
#include "renderer/backend/IFramebuffer.h"
|
2025-07-06 11:15:27 -07:00
|
|
|
#include "renderer/backend/IShaderProgram.h"
|
2025-07-10 12:03:11 -07:00
|
|
|
#include "renderer/backend/ITexture.h"
|
2025-07-06 11:15:27 -07:00
|
|
|
#include "renderer/backend/PipelineState.h"
|
|
|
|
|
#include "renderer/backend/Sampler.h"
|
2022-02-18 09:33:12 -08:00
|
|
|
#include "renderer/backend/gl/Buffer.h"
|
2022-02-05 08:59:23 -08:00
|
|
|
#include "renderer/backend/gl/Device.h"
|
|
|
|
|
#include "renderer/backend/gl/Framebuffer.h"
|
2022-01-19 09:28:47 -08:00
|
|
|
#include "renderer/backend/gl/Mapping.h"
|
2022-12-31 10:29:44 -08:00
|
|
|
#include "renderer/backend/gl/PipelineState.h"
|
2022-03-14 15:16:14 -07:00
|
|
|
#include "renderer/backend/gl/ShaderProgram.h"
|
2022-01-05 06:49:54 -08:00
|
|
|
#include "renderer/backend/gl/Texture.h"
|
|
|
|
|
|
2022-02-13 11:30:28 -08:00
|
|
|
#include <algorithm>
|
2022-02-18 09:33:12 -08:00
|
|
|
#include <cstring>
|
2022-02-05 08:59:23 -08:00
|
|
|
#include <limits>
|
2025-07-10 12:03:11 -07:00
|
|
|
#include <vector>
|
2022-02-05 08:59:23 -08:00
|
|
|
|
2022-01-05 06:49:54 -08:00
|
|
|
namespace Renderer
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
namespace Backend
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
namespace GL
|
|
|
|
|
{
|
|
|
|
|
|
2022-02-01 08:38:55 -08:00
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
|
2022-12-31 10:29:44 -08:00
|
|
|
bool operator==(const SStencilOpState& lhs, const SStencilOpState& rhs)
|
2022-02-01 08:38:55 -08:00
|
|
|
{
|
|
|
|
|
return
|
|
|
|
|
lhs.failOp == rhs.failOp &&
|
|
|
|
|
lhs.passOp == rhs.passOp &&
|
|
|
|
|
lhs.depthFailOp == rhs.depthFailOp &&
|
|
|
|
|
lhs.compareOp == rhs.compareOp;
|
|
|
|
|
}
|
2022-12-31 10:29:44 -08:00
|
|
|
bool operator!=(const SStencilOpState& lhs, const SStencilOpState& rhs)
|
2022-02-01 08:38:55 -08:00
|
|
|
{
|
|
|
|
|
return !operator==(lhs, rhs);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-01 09:58:21 -08:00
|
|
|
bool operator==(
|
2022-02-26 15:17:48 -08:00
|
|
|
const CDeviceCommandContext::Rect& lhs,
|
|
|
|
|
const CDeviceCommandContext::Rect& rhs)
|
2022-02-01 09:58:21 -08:00
|
|
|
{
|
|
|
|
|
return
|
|
|
|
|
lhs.x == rhs.x && lhs.y == rhs.y &&
|
|
|
|
|
lhs.width == rhs.width && lhs.height == rhs.height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool operator!=(
|
2022-02-26 15:17:48 -08:00
|
|
|
const CDeviceCommandContext::Rect& lhs,
|
|
|
|
|
const CDeviceCommandContext::Rect& rhs)
|
2022-02-01 09:58:21 -08:00
|
|
|
{
|
|
|
|
|
return !operator==(lhs, rhs);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-05 08:59:23 -08:00
|
|
|
void ApplyDepthMask(const bool depthWriteEnabled)
|
|
|
|
|
{
|
|
|
|
|
glDepthMask(depthWriteEnabled ? GL_TRUE : GL_FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ApplyColorMask(const uint8_t colorWriteMask)
|
|
|
|
|
{
|
|
|
|
|
glColorMask(
|
|
|
|
|
(colorWriteMask & ColorWriteMask::RED) != 0 ? GL_TRUE : GL_FALSE,
|
|
|
|
|
(colorWriteMask & ColorWriteMask::GREEN) != 0 ? GL_TRUE : GL_FALSE,
|
|
|
|
|
(colorWriteMask & ColorWriteMask::BLUE) != 0 ? GL_TRUE : GL_FALSE,
|
|
|
|
|
(colorWriteMask & ColorWriteMask::ALPHA) != 0 ? GL_TRUE : GL_FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ApplyStencilMask(const uint32_t stencilWriteMask)
|
|
|
|
|
{
|
|
|
|
|
glStencilMask(stencilWriteMask);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-18 09:33:12 -08:00
|
|
|
GLenum BufferTypeToGLTarget(const CBuffer::Type type)
|
|
|
|
|
{
|
|
|
|
|
GLenum target = GL_ARRAY_BUFFER;
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case CBuffer::Type::VERTEX:
|
|
|
|
|
target = GL_ARRAY_BUFFER;
|
|
|
|
|
break;
|
|
|
|
|
case CBuffer::Type::INDEX:
|
|
|
|
|
target = GL_ELEMENT_ARRAY_BUFFER;
|
|
|
|
|
break;
|
2023-01-10 12:22:20 -08:00
|
|
|
case CBuffer::Type::UNIFORM:
|
2025-01-04 06:13:10 -08:00
|
|
|
#if !CONFIG2_GLES
|
2024-12-09 13:47:15 -08:00
|
|
|
target = GL_UNIFORM_BUFFER;
|
2025-01-04 06:13:10 -08:00
|
|
|
#else
|
|
|
|
|
debug_warn("Unsupported buffer type.");
|
|
|
|
|
#endif
|
2024-12-09 13:47:15 -08:00
|
|
|
break;
|
|
|
|
|
case CBuffer::Type::UPLOAD:
|
2023-01-10 12:22:20 -08:00
|
|
|
debug_warn("Unsupported buffer type.");
|
|
|
|
|
break;
|
2022-02-18 09:33:12 -08:00
|
|
|
};
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-08 15:45:54 -07:00
|
|
|
void UploadDynamicBufferRegionImpl(
|
|
|
|
|
const GLenum target, const uint32_t bufferSize,
|
|
|
|
|
const uint32_t dataOffset, const uint32_t dataSize,
|
2022-02-18 09:33:12 -08:00
|
|
|
const CDeviceCommandContext::UploadBufferFunction& uploadFunction)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(dataOffset < dataSize);
|
2022-05-08 15:45:54 -07:00
|
|
|
// Tell the driver that it can reallocate the whole VBO
|
|
|
|
|
glBufferDataARB(target, bufferSize, nullptr, GL_DYNAMIC_DRAW);
|
|
|
|
|
ogl_WarnIfError();
|
|
|
|
|
|
2022-02-18 09:33:12 -08:00
|
|
|
while (true)
|
|
|
|
|
{
|
2022-05-08 15:45:54 -07:00
|
|
|
// (In theory, glMapBufferRange with GL_MAP_INVALIDATE_BUFFER_BIT could be used
|
|
|
|
|
// here instead of glBufferData(..., NULL, ...) plus glMapBuffer(), but with
|
|
|
|
|
// current Intel Windows GPU drivers (as of 2015-01) it's much faster if you do
|
|
|
|
|
// the explicit glBufferData.)
|
2022-02-18 09:33:12 -08:00
|
|
|
void* mappedData = glMapBufferARB(target, GL_WRITE_ONLY);
|
|
|
|
|
if (mappedData == nullptr)
|
|
|
|
|
{
|
|
|
|
|
// This shouldn't happen unless we run out of virtual address space
|
|
|
|
|
LOGERROR("glMapBuffer failed");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uploadFunction(static_cast<u8*>(mappedData) + dataOffset);
|
|
|
|
|
|
|
|
|
|
if (glUnmapBufferARB(target) == GL_TRUE)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// Unmap might fail on e.g. resolution switches, so just try again
|
|
|
|
|
// and hope it will eventually succeed
|
|
|
|
|
LOGMESSAGE("glUnmapBuffer failed, trying again...\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-26 13:55:17 -08:00
|
|
|
/**
|
|
|
|
|
* In case we don't need a framebuffer content (because of the following clear
|
|
|
|
|
* or overwriting by a shader) we might give a hint to a driver via
|
|
|
|
|
* glInvalidateFramebuffer.
|
|
|
|
|
*/
|
|
|
|
|
void InvalidateFramebuffer(
|
|
|
|
|
CFramebuffer* framebuffer, const bool color, const bool depthStencil)
|
|
|
|
|
{
|
|
|
|
|
GLsizei numberOfAttachments = 0;
|
|
|
|
|
GLenum attachments[8];
|
|
|
|
|
const bool isBackbuffer = framebuffer->GetHandle() == 0;
|
|
|
|
|
if (color && (framebuffer->GetAttachmentMask() & GL_COLOR_BUFFER_BIT))
|
|
|
|
|
{
|
|
|
|
|
if (isBackbuffer)
|
|
|
|
|
#if CONFIG2_GLES
|
|
|
|
|
attachments[numberOfAttachments++] = GL_COLOR_EXT;
|
|
|
|
|
#else
|
|
|
|
|
attachments[numberOfAttachments++] = GL_COLOR;
|
|
|
|
|
#endif
|
|
|
|
|
else
|
|
|
|
|
attachments[numberOfAttachments++] = GL_COLOR_ATTACHMENT0;
|
|
|
|
|
}
|
|
|
|
|
if (depthStencil)
|
|
|
|
|
{
|
|
|
|
|
if (isBackbuffer)
|
|
|
|
|
{
|
|
|
|
|
if (framebuffer->GetAttachmentMask() & GL_DEPTH_BUFFER_BIT)
|
|
|
|
|
#if CONFIG2_GLES
|
|
|
|
|
attachments[numberOfAttachments++] = GL_DEPTH_EXT;
|
|
|
|
|
#else
|
|
|
|
|
attachments[numberOfAttachments++] = GL_DEPTH;
|
|
|
|
|
#endif
|
|
|
|
|
if (framebuffer->GetAttachmentMask() & GL_STENCIL_BUFFER_BIT)
|
|
|
|
|
#if CONFIG2_GLES
|
|
|
|
|
attachments[numberOfAttachments++] = GL_STENCIL_EXT;
|
|
|
|
|
#else
|
|
|
|
|
attachments[numberOfAttachments++] = GL_STENCIL;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (framebuffer->GetAttachmentMask() & GL_DEPTH_BUFFER_BIT)
|
|
|
|
|
attachments[numberOfAttachments++] = GL_DEPTH_ATTACHMENT;
|
|
|
|
|
if (framebuffer->GetAttachmentMask() & GL_STENCIL_BUFFER_BIT)
|
|
|
|
|
attachments[numberOfAttachments++] = GL_STENCIL_ATTACHMENT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (numberOfAttachments > 0)
|
|
|
|
|
{
|
|
|
|
|
#if CONFIG2_GLES
|
|
|
|
|
glDiscardFramebufferEXT(GL_FRAMEBUFFER_EXT, numberOfAttachments, attachments);
|
|
|
|
|
#else
|
|
|
|
|
glInvalidateFramebuffer(GL_FRAMEBUFFER_EXT, numberOfAttachments, attachments);
|
|
|
|
|
#endif
|
|
|
|
|
ogl_WarnIfError();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-01 08:38:55 -08:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2022-01-05 06:49:54 -08:00
|
|
|
// static
|
2022-02-05 08:59:23 -08:00
|
|
|
std::unique_ptr<CDeviceCommandContext> CDeviceCommandContext::Create(CDevice* device)
|
2022-01-05 06:49:54 -08:00
|
|
|
{
|
2022-02-05 08:59:23 -08:00
|
|
|
std::unique_ptr<CDeviceCommandContext> deviceCommandContext(new CDeviceCommandContext(device));
|
2022-11-26 13:55:17 -08:00
|
|
|
deviceCommandContext->m_Framebuffer = device->GetCurrentBackbuffer(
|
|
|
|
|
Renderer::Backend::AttachmentLoadOp::DONT_CARE,
|
|
|
|
|
Renderer::Backend::AttachmentStoreOp::DONT_CARE,
|
|
|
|
|
Renderer::Backend::AttachmentLoadOp::DONT_CARE,
|
|
|
|
|
Renderer::Backend::AttachmentStoreOp::DONT_CARE)->As<CFramebuffer>();
|
2022-01-19 09:28:47 -08:00
|
|
|
deviceCommandContext->ResetStates();
|
2022-01-05 06:49:54 -08:00
|
|
|
return deviceCommandContext;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-05 08:59:23 -08:00
|
|
|
CDeviceCommandContext::CDeviceCommandContext(CDevice* device)
|
|
|
|
|
: m_Device(device)
|
|
|
|
|
{
|
2022-02-13 11:30:28 -08:00
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
2022-05-02 13:57:22 -07:00
|
|
|
for (BindUnit& unit : m_BoundTextures)
|
2022-04-26 11:25:52 -07:00
|
|
|
{
|
2022-05-02 13:57:22 -07:00
|
|
|
unit.target = GL_TEXTURE_2D;
|
|
|
|
|
unit.handle = 0;
|
2022-04-26 11:25:52 -07:00
|
|
|
}
|
2022-04-23 13:11:14 -07:00
|
|
|
for (size_t index = 0; index < m_VertexAttributeFormat.size(); ++index)
|
|
|
|
|
{
|
|
|
|
|
m_VertexAttributeFormat[index].active = false;
|
|
|
|
|
m_VertexAttributeFormat[index].initialized = false;
|
|
|
|
|
m_VertexAttributeFormat[index].bindingSlot = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t index = 0; index < m_BoundBuffers.size(); ++index)
|
|
|
|
|
{
|
|
|
|
|
const CBuffer::Type type = static_cast<CBuffer::Type>(index);
|
2025-04-12 07:27:52 -07:00
|
|
|
// Currently we don't support upload buffers for GL.
|
|
|
|
|
if (type == CBuffer::Type::UPLOAD)
|
|
|
|
|
continue;
|
2022-04-23 13:11:14 -07:00
|
|
|
const GLenum target = BufferTypeToGLTarget(type);
|
|
|
|
|
const GLuint handle = 0;
|
|
|
|
|
m_BoundBuffers[index].first = target;
|
|
|
|
|
m_BoundBuffers[index].second = handle;
|
|
|
|
|
}
|
2022-02-05 08:59:23 -08:00
|
|
|
}
|
2022-01-05 06:49:54 -08:00
|
|
|
|
|
|
|
|
CDeviceCommandContext::~CDeviceCommandContext() = default;
|
|
|
|
|
|
2022-05-08 15:02:46 -07:00
|
|
|
IDevice* CDeviceCommandContext::GetDevice()
|
|
|
|
|
{
|
|
|
|
|
return m_Device;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-19 09:28:47 -08:00
|
|
|
void CDeviceCommandContext::SetGraphicsPipelineState(
|
2022-12-31 10:29:44 -08:00
|
|
|
const SGraphicsPipelineStateDesc& pipelineState)
|
2022-01-19 09:28:47 -08:00
|
|
|
{
|
2024-01-17 11:40:27 -08:00
|
|
|
ENSURE(!pipelineState.shaderProgram || m_InsideFramebufferPass);
|
2022-12-31 10:29:44 -08:00
|
|
|
SetGraphicsPipelineStateImpl(pipelineState, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetGraphicsPipelineState(
|
|
|
|
|
IGraphicsPipelineState* pipelineState)
|
|
|
|
|
{
|
2024-01-17 11:40:27 -08:00
|
|
|
ENSURE(!pipelineState->GetShaderProgram() || m_InsideFramebufferPass);
|
2022-12-31 10:29:44 -08:00
|
|
|
ENSURE(pipelineState);
|
|
|
|
|
SetGraphicsPipelineStateImpl(
|
|
|
|
|
pipelineState->As<CGraphicsPipelineState>()->GetDesc(), false);
|
2022-01-19 09:28:47 -08:00
|
|
|
}
|
|
|
|
|
|
2024-01-17 11:40:27 -08:00
|
|
|
void CDeviceCommandContext::SetComputePipelineState(
|
|
|
|
|
IComputePipelineState* pipelineState)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_InsideComputePass);
|
|
|
|
|
ENSURE(pipelineState);
|
|
|
|
|
const SComputePipelineStateDesc& desc = pipelineState->As<CComputePipelineState>()->GetDesc();
|
|
|
|
|
if (m_ComputePipelineStateDesc.shaderProgram != desc.shaderProgram)
|
|
|
|
|
{
|
|
|
|
|
CShaderProgram* currentShaderProgram = nullptr;
|
|
|
|
|
if (m_ComputePipelineStateDesc.shaderProgram)
|
|
|
|
|
currentShaderProgram = m_ComputePipelineStateDesc.shaderProgram->As<CShaderProgram>();
|
|
|
|
|
CShaderProgram* nextShaderProgram = nullptr;
|
|
|
|
|
if (desc.shaderProgram)
|
|
|
|
|
nextShaderProgram = desc.shaderProgram->As<CShaderProgram>();
|
|
|
|
|
|
|
|
|
|
if (nextShaderProgram)
|
|
|
|
|
nextShaderProgram->Bind(currentShaderProgram);
|
|
|
|
|
else if (currentShaderProgram)
|
|
|
|
|
currentShaderProgram->Unbind();
|
|
|
|
|
|
|
|
|
|
m_ShaderProgram = nextShaderProgram;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-05 06:49:54 -08:00
|
|
|
void CDeviceCommandContext::UploadTexture(
|
2022-05-08 15:02:46 -07:00
|
|
|
ITexture* texture, const Format format,
|
2022-01-07 12:00:41 -08:00
|
|
|
const void* data, const size_t dataSize,
|
|
|
|
|
const uint32_t level, const uint32_t layer)
|
2022-01-05 06:49:54 -08:00
|
|
|
{
|
2022-01-07 12:00:41 -08:00
|
|
|
UploadTextureRegion(texture, format, data, dataSize,
|
2022-02-13 11:30:28 -08:00
|
|
|
0, 0,
|
|
|
|
|
std::max(1u, texture->GetWidth() >> level),
|
|
|
|
|
std::max(1u, texture->GetHeight() >> level),
|
|
|
|
|
level, layer);
|
2022-01-05 06:49:54 -08:00
|
|
|
}
|
|
|
|
|
|
2022-01-07 12:00:41 -08:00
|
|
|
void CDeviceCommandContext::UploadTextureRegion(
|
2022-05-08 15:02:46 -07:00
|
|
|
ITexture* destinationTexture, const Format dataFormat,
|
2022-01-07 12:00:41 -08:00
|
|
|
const void* data, const size_t dataSize,
|
|
|
|
|
const uint32_t xOffset, const uint32_t yOffset,
|
|
|
|
|
const uint32_t width, const uint32_t height,
|
|
|
|
|
const uint32_t level, const uint32_t layer)
|
2022-01-05 06:49:54 -08:00
|
|
|
{
|
2022-05-08 15:02:46 -07:00
|
|
|
ENSURE(destinationTexture);
|
|
|
|
|
CTexture* texture = destinationTexture->As<CTexture>();
|
2022-10-12 16:19:27 -07:00
|
|
|
ENSURE(texture->GetUsage() & Renderer::Backend::ITexture::Usage::TRANSFER_DST);
|
2022-02-13 11:30:28 -08:00
|
|
|
ENSURE(width > 0 && height > 0);
|
2022-01-05 06:49:54 -08:00
|
|
|
if (texture->GetType() == CTexture::Type::TEXTURE_2D)
|
|
|
|
|
{
|
2022-02-13 11:30:28 -08:00
|
|
|
ENSURE(layer == 0);
|
2022-03-06 14:14:57 -08:00
|
|
|
if (texture->GetFormat() == Format::R8G8B8A8_UNORM ||
|
|
|
|
|
texture->GetFormat() == Format::R8G8B8_UNORM ||
|
2022-05-26 09:36:57 -07:00
|
|
|
#if !CONFIG2_GLES
|
|
|
|
|
texture->GetFormat() == Format::R8_UNORM ||
|
|
|
|
|
#endif
|
2022-03-06 14:14:57 -08:00
|
|
|
texture->GetFormat() == Format::A8_UNORM)
|
2022-01-05 06:49:54 -08:00
|
|
|
{
|
2022-01-08 05:44:40 -08:00
|
|
|
ENSURE(texture->GetFormat() == dataFormat);
|
2022-02-13 11:30:28 -08:00
|
|
|
size_t bytesPerPixel = 4;
|
|
|
|
|
GLenum pixelFormat = GL_RGBA;
|
|
|
|
|
switch (dataFormat)
|
|
|
|
|
{
|
2022-03-06 14:14:57 -08:00
|
|
|
case Format::R8G8B8A8_UNORM:
|
2022-02-13 11:30:28 -08:00
|
|
|
break;
|
2022-03-06 14:14:57 -08:00
|
|
|
case Format::R8G8B8_UNORM:
|
2022-02-13 11:30:28 -08:00
|
|
|
pixelFormat = GL_RGB;
|
|
|
|
|
bytesPerPixel = 3;
|
|
|
|
|
break;
|
2022-05-26 09:36:57 -07:00
|
|
|
#if !CONFIG2_GLES
|
|
|
|
|
case Format::R8_UNORM:
|
|
|
|
|
pixelFormat = GL_RED;
|
|
|
|
|
bytesPerPixel = 1;
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
2022-03-06 14:14:57 -08:00
|
|
|
case Format::A8_UNORM:
|
2022-02-13 11:30:28 -08:00
|
|
|
pixelFormat = GL_ALPHA;
|
|
|
|
|
bytesPerPixel = 1;
|
|
|
|
|
break;
|
2022-03-06 14:14:57 -08:00
|
|
|
case Format::L8_UNORM:
|
2022-02-13 11:30:28 -08:00
|
|
|
pixelFormat = GL_LUMINANCE;
|
|
|
|
|
bytesPerPixel = 1;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
debug_warn("Unexpected format.");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
ENSURE(dataSize == width * height * bytesPerPixel);
|
2022-01-05 06:49:54 -08:00
|
|
|
|
2022-02-13 11:30:28 -08:00
|
|
|
ScopedBind scopedBind(this, GL_TEXTURE_2D, texture->GetHandle());
|
2022-01-07 12:00:41 -08:00
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, level,
|
2022-01-05 06:49:54 -08:00
|
|
|
xOffset, yOffset, width, height,
|
2022-02-13 11:30:28 -08:00
|
|
|
pixelFormat, GL_UNSIGNED_BYTE, data);
|
|
|
|
|
ogl_WarnIfError();
|
|
|
|
|
}
|
|
|
|
|
else if (
|
2022-03-06 14:14:57 -08:00
|
|
|
texture->GetFormat() == Format::BC1_RGB_UNORM ||
|
|
|
|
|
texture->GetFormat() == Format::BC1_RGBA_UNORM ||
|
|
|
|
|
texture->GetFormat() == Format::BC2_UNORM ||
|
|
|
|
|
texture->GetFormat() == Format::BC3_UNORM)
|
2022-02-13 11:30:28 -08:00
|
|
|
{
|
|
|
|
|
ENSURE(xOffset == 0 && yOffset == 0);
|
|
|
|
|
ENSURE(texture->GetFormat() == dataFormat);
|
|
|
|
|
// TODO: add data size check.
|
2022-01-05 06:49:54 -08:00
|
|
|
|
2022-02-13 11:30:28 -08:00
|
|
|
GLenum internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
|
|
|
|
|
switch (texture->GetFormat())
|
|
|
|
|
{
|
2022-03-06 14:14:57 -08:00
|
|
|
case Format::BC1_RGBA_UNORM:
|
2022-02-13 11:30:28 -08:00
|
|
|
internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
|
|
|
|
break;
|
2022-03-06 14:14:57 -08:00
|
|
|
case Format::BC2_UNORM:
|
2022-02-13 11:30:28 -08:00
|
|
|
internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
|
|
|
|
break;
|
2022-03-06 14:14:57 -08:00
|
|
|
case Format::BC3_UNORM:
|
2022-02-13 11:30:28 -08:00
|
|
|
internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ScopedBind scopedBind(this, GL_TEXTURE_2D, texture->GetHandle());
|
|
|
|
|
glCompressedTexImage2DARB(GL_TEXTURE_2D, level, internalFormat, width, height, 0, dataSize, data);
|
2022-01-05 06:49:54 -08:00
|
|
|
ogl_WarnIfError();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
debug_warn("Unsupported format");
|
|
|
|
|
}
|
2022-01-07 12:00:41 -08:00
|
|
|
else if (texture->GetType() == CTexture::Type::TEXTURE_CUBE)
|
|
|
|
|
{
|
2022-03-06 14:14:57 -08:00
|
|
|
if (texture->GetFormat() == Format::R8G8B8A8_UNORM)
|
2022-01-07 12:00:41 -08:00
|
|
|
{
|
2022-01-08 05:44:40 -08:00
|
|
|
ENSURE(texture->GetFormat() == dataFormat);
|
|
|
|
|
ENSURE(level == 0 && layer < 6);
|
2022-01-07 12:00:41 -08:00
|
|
|
ENSURE(xOffset == 0 && yOffset == 0 && texture->GetWidth() == width && texture->GetHeight() == height);
|
|
|
|
|
const size_t bpp = 4;
|
|
|
|
|
ENSURE(dataSize == width * height * bpp);
|
|
|
|
|
|
|
|
|
|
// The order of layers should be the following:
|
|
|
|
|
// front, back, top, bottom, right, left
|
|
|
|
|
static const GLenum targets[6] =
|
|
|
|
|
{
|
|
|
|
|
GL_TEXTURE_CUBE_MAP_POSITIVE_X,
|
|
|
|
|
GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
|
|
|
|
|
GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
|
|
|
|
|
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
|
|
|
|
|
GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
|
|
|
|
|
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
|
|
|
|
|
};
|
|
|
|
|
|
2022-02-13 11:30:28 -08:00
|
|
|
ScopedBind scopedBind(this, GL_TEXTURE_CUBE_MAP, texture->GetHandle());
|
2022-01-07 12:00:41 -08:00
|
|
|
glTexImage2D(targets[layer], level, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
|
|
|
|
ogl_WarnIfError();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
debug_warn("Unsupported format");
|
|
|
|
|
}
|
2022-01-05 06:49:54 -08:00
|
|
|
else
|
|
|
|
|
debug_warn("Unsupported type");
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-08 15:02:46 -07:00
|
|
|
void CDeviceCommandContext::UploadBuffer(IBuffer* buffer, const void* data, const uint32_t dataSize)
|
2022-02-18 09:33:12 -08:00
|
|
|
{
|
2022-10-29 16:20:04 -07:00
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
2024-11-01 05:30:12 -07:00
|
|
|
UploadBufferRegion(buffer, data, 0, dataSize);
|
2022-02-18 09:33:12 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::UploadBuffer(
|
2022-05-08 15:02:46 -07:00
|
|
|
IBuffer* buffer, const UploadBufferFunction& uploadFunction)
|
2022-02-18 09:33:12 -08:00
|
|
|
{
|
2022-10-29 16:20:04 -07:00
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
2022-02-18 09:33:12 -08:00
|
|
|
UploadBufferRegion(buffer, 0, buffer->GetSize(), uploadFunction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::UploadBufferRegion(
|
2022-05-08 15:02:46 -07:00
|
|
|
IBuffer* buffer, const void* data, const uint32_t dataOffset, const uint32_t dataSize)
|
2022-02-18 09:33:12 -08:00
|
|
|
{
|
2022-10-29 16:20:04 -07:00
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
2022-02-18 09:33:12 -08:00
|
|
|
ENSURE(data);
|
|
|
|
|
ENSURE(dataOffset + dataSize <= buffer->GetSize());
|
|
|
|
|
const GLenum target = BufferTypeToGLTarget(buffer->GetType());
|
2022-05-08 15:02:46 -07:00
|
|
|
ScopedBufferBind scopedBufferBind(this, buffer->As<CBuffer>());
|
2024-12-09 13:47:15 -08:00
|
|
|
// Uniform buffers is a relatively new feature so we don't need to use a
|
|
|
|
|
// dynamic upload.
|
|
|
|
|
if (buffer->IsDynamic() && buffer->GetType() != IBuffer::Type::UNIFORM)
|
2022-02-18 09:33:12 -08:00
|
|
|
{
|
2022-05-08 15:45:54 -07:00
|
|
|
UploadDynamicBufferRegionImpl(target, buffer->GetSize(), dataOffset, dataSize, [data, dataSize](u8* mappedData)
|
2022-02-18 09:33:12 -08:00
|
|
|
{
|
|
|
|
|
std::memcpy(mappedData, data, dataSize);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
glBufferSubDataARB(target, dataOffset, dataSize, data);
|
2022-05-02 14:52:21 -07:00
|
|
|
ogl_WarnIfError();
|
2022-02-18 09:33:12 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::UploadBufferRegion(
|
2022-05-08 15:02:46 -07:00
|
|
|
IBuffer* buffer, const uint32_t dataOffset, const uint32_t dataSize,
|
2022-02-18 09:33:12 -08:00
|
|
|
const UploadBufferFunction& uploadFunction)
|
|
|
|
|
{
|
2022-10-29 16:20:04 -07:00
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
2022-02-18 09:33:12 -08:00
|
|
|
ENSURE(dataOffset + dataSize <= buffer->GetSize());
|
|
|
|
|
const GLenum target = BufferTypeToGLTarget(buffer->GetType());
|
2022-05-08 15:02:46 -07:00
|
|
|
ScopedBufferBind scopedBufferBind(this, buffer->As<CBuffer>());
|
2022-02-18 09:33:12 -08:00
|
|
|
ENSURE(buffer->IsDynamic());
|
2022-05-08 15:45:54 -07:00
|
|
|
UploadDynamicBufferRegionImpl(target, buffer->GetSize(), dataOffset, dataSize, uploadFunction);
|
2022-02-18 09:33:12 -08:00
|
|
|
}
|
|
|
|
|
|
2025-06-03 05:13:41 -07:00
|
|
|
void CDeviceCommandContext::InsertTimestampQuery(const uint32_t handle, const bool /*isScopeBegin*/)
|
2025-04-16 13:38:32 -07:00
|
|
|
{
|
|
|
|
|
// GL can have the only one command context so we can call commands on
|
|
|
|
|
// the deivce side.
|
|
|
|
|
m_Device->InsertTimestampQuery(handle);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-18 14:38:45 -08:00
|
|
|
void CDeviceCommandContext::BeginScopedLabel(const char* name)
|
|
|
|
|
{
|
|
|
|
|
if (!m_Device->GetCapabilities().debugScopedLabels)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
++m_ScopedLabelDepth;
|
|
|
|
|
glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0x0AD, -1, name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::EndScopedLabel()
|
|
|
|
|
{
|
|
|
|
|
if (!m_Device->GetCapabilities().debugScopedLabels)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ENSURE(m_ScopedLabelDepth > 0);
|
|
|
|
|
--m_ScopedLabelDepth;
|
|
|
|
|
glPopDebugGroup();
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-02 13:57:22 -07:00
|
|
|
void CDeviceCommandContext::BindTexture(
|
|
|
|
|
const uint32_t unit, const GLenum target, const GLuint handle)
|
2022-02-13 11:30:28 -08:00
|
|
|
{
|
|
|
|
|
ENSURE(unit < m_BoundTextures.size());
|
|
|
|
|
#if CONFIG2_GLES
|
|
|
|
|
ENSURE(target == GL_TEXTURE_2D || target == GL_TEXTURE_CUBE_MAP);
|
|
|
|
|
#else
|
|
|
|
|
ENSURE(target == GL_TEXTURE_2D || target == GL_TEXTURE_CUBE_MAP || target == GL_TEXTURE_2D_MULTISAMPLE);
|
|
|
|
|
#endif
|
|
|
|
|
if (m_ActiveTextureUnit != unit)
|
|
|
|
|
{
|
|
|
|
|
glActiveTexture(GL_TEXTURE0 + unit);
|
|
|
|
|
m_ActiveTextureUnit = unit;
|
|
|
|
|
}
|
2022-05-02 13:57:22 -07:00
|
|
|
if (m_BoundTextures[unit].target == target && m_BoundTextures[unit].handle == handle)
|
|
|
|
|
return;
|
|
|
|
|
if (m_BoundTextures[unit].target != target && m_BoundTextures[unit].target && m_BoundTextures[unit].handle)
|
|
|
|
|
glBindTexture(m_BoundTextures[unit].target, 0);
|
|
|
|
|
if (m_BoundTextures[unit].handle != handle)
|
2022-02-13 11:30:28 -08:00
|
|
|
glBindTexture(target, handle);
|
2022-05-02 14:52:21 -07:00
|
|
|
ogl_WarnIfError();
|
2022-02-13 11:30:28 -08:00
|
|
|
m_BoundTextures[unit] = {target, handle};
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-08 15:02:46 -07:00
|
|
|
void CDeviceCommandContext::BindBuffer(const IBuffer::Type type, CBuffer* buffer)
|
2022-02-18 09:33:12 -08:00
|
|
|
{
|
2022-03-02 23:09:59 -08:00
|
|
|
ENSURE(!buffer || buffer->GetType() == type);
|
2022-05-08 15:02:46 -07:00
|
|
|
if (type == IBuffer::Type::VERTEX)
|
2022-04-23 13:11:14 -07:00
|
|
|
{
|
|
|
|
|
if (m_VertexBuffer == buffer)
|
|
|
|
|
return;
|
|
|
|
|
m_VertexBuffer = buffer;
|
|
|
|
|
}
|
2022-05-08 15:02:46 -07:00
|
|
|
else if (type == IBuffer::Type::INDEX)
|
2022-03-02 23:09:59 -08:00
|
|
|
{
|
|
|
|
|
if (!buffer)
|
|
|
|
|
m_IndexBuffer = nullptr;
|
|
|
|
|
m_IndexBufferData = nullptr;
|
|
|
|
|
}
|
2022-04-23 13:11:14 -07:00
|
|
|
const GLenum target = BufferTypeToGLTarget(type);
|
|
|
|
|
const GLuint handle = buffer ? buffer->GetHandle() : 0;
|
|
|
|
|
glBindBufferARB(target, handle);
|
2022-05-02 14:52:21 -07:00
|
|
|
ogl_WarnIfError();
|
2022-04-23 13:11:14 -07:00
|
|
|
const size_t cacheIndex = static_cast<size_t>(type);
|
|
|
|
|
ENSURE(cacheIndex < m_BoundBuffers.size());
|
|
|
|
|
m_BoundBuffers[cacheIndex].second = handle;
|
2022-02-18 09:33:12 -08:00
|
|
|
}
|
|
|
|
|
|
2022-03-17 11:17:11 -07:00
|
|
|
void CDeviceCommandContext::OnTextureDestroy(CTexture* texture)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(texture);
|
|
|
|
|
for (size_t index = 0; index < m_BoundTextures.size(); ++index)
|
2022-05-02 13:57:22 -07:00
|
|
|
if (m_BoundTextures[index].handle == texture->GetHandle())
|
2022-03-17 11:17:11 -07:00
|
|
|
BindTexture(index, GL_TEXTURE_2D, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-19 09:28:47 -08:00
|
|
|
void CDeviceCommandContext::Flush()
|
|
|
|
|
{
|
2022-05-02 13:57:22 -07:00
|
|
|
ENSURE(m_ScopedLabelDepth == 0);
|
2024-01-17 11:40:27 -08:00
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
|
|
|
|
ENSURE(!m_InsideComputePass);
|
2022-05-02 13:57:22 -07:00
|
|
|
|
|
|
|
|
GPU_SCOPED_LABEL(this, "CDeviceCommandContext::Flush");
|
|
|
|
|
|
2022-01-19 09:28:47 -08:00
|
|
|
ResetStates();
|
2022-02-13 11:30:28 -08:00
|
|
|
|
2022-03-02 23:09:59 -08:00
|
|
|
m_IndexBuffer = nullptr;
|
|
|
|
|
m_IndexBufferData = nullptr;
|
|
|
|
|
|
2022-05-02 13:57:22 -07:00
|
|
|
for (size_t unit = 0; unit < m_BoundTextures.size(); ++unit)
|
|
|
|
|
{
|
|
|
|
|
if (m_BoundTextures[unit].handle)
|
|
|
|
|
BindTexture(unit, GL_TEXTURE_2D, 0);
|
|
|
|
|
}
|
2022-03-02 23:09:59 -08:00
|
|
|
BindBuffer(CBuffer::Type::INDEX, nullptr);
|
|
|
|
|
BindBuffer(CBuffer::Type::VERTEX, nullptr);
|
2022-01-19 09:28:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::ResetStates()
|
|
|
|
|
{
|
|
|
|
|
SetGraphicsPipelineStateImpl(MakeDefaultGraphicsPipelineStateDesc(), true);
|
2022-02-01 09:58:21 -08:00
|
|
|
SetScissors(0, nullptr);
|
2022-11-26 13:55:17 -08:00
|
|
|
m_Framebuffer = m_Device->GetCurrentBackbuffer(
|
|
|
|
|
Renderer::Backend::AttachmentLoadOp::DONT_CARE,
|
|
|
|
|
Renderer::Backend::AttachmentStoreOp::DONT_CARE,
|
|
|
|
|
Renderer::Backend::AttachmentLoadOp::DONT_CARE,
|
|
|
|
|
Renderer::Backend::AttachmentStoreOp::DONT_CARE)->As<CFramebuffer>();
|
2022-10-12 14:51:27 -07:00
|
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_Framebuffer->GetHandle());
|
|
|
|
|
ogl_WarnIfError();
|
2022-01-19 09:28:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetGraphicsPipelineStateImpl(
|
2022-12-31 10:29:44 -08:00
|
|
|
const SGraphicsPipelineStateDesc& pipelineStateDesc, const bool force)
|
2022-01-19 09:28:47 -08:00
|
|
|
{
|
2022-03-14 15:16:14 -07:00
|
|
|
ENSURE(!m_InsidePass);
|
|
|
|
|
|
|
|
|
|
if (m_GraphicsPipelineStateDesc.shaderProgram != pipelineStateDesc.shaderProgram)
|
|
|
|
|
{
|
|
|
|
|
CShaderProgram* currentShaderProgram = nullptr;
|
|
|
|
|
if (m_GraphicsPipelineStateDesc.shaderProgram)
|
|
|
|
|
{
|
|
|
|
|
currentShaderProgram =
|
|
|
|
|
static_cast<CShaderProgram*>(m_GraphicsPipelineStateDesc.shaderProgram);
|
|
|
|
|
}
|
|
|
|
|
CShaderProgram* nextShaderProgram = nullptr;
|
|
|
|
|
if (pipelineStateDesc.shaderProgram)
|
|
|
|
|
{
|
|
|
|
|
nextShaderProgram =
|
|
|
|
|
static_cast<CShaderProgram*>(pipelineStateDesc.shaderProgram);
|
2022-04-23 13:11:14 -07:00
|
|
|
for (size_t index = 0; index < m_VertexAttributeFormat.size(); ++index)
|
|
|
|
|
{
|
|
|
|
|
const VertexAttributeStream stream = static_cast<VertexAttributeStream>(index);
|
|
|
|
|
m_VertexAttributeFormat[index].active = nextShaderProgram->IsStreamActive(stream);
|
|
|
|
|
m_VertexAttributeFormat[index].initialized = false;
|
|
|
|
|
m_VertexAttributeFormat[index].bindingSlot = std::numeric_limits<uint32_t>::max();
|
|
|
|
|
}
|
2022-03-14 15:16:14 -07:00
|
|
|
}
|
|
|
|
|
if (nextShaderProgram)
|
|
|
|
|
nextShaderProgram->Bind(currentShaderProgram);
|
|
|
|
|
else if (currentShaderProgram)
|
|
|
|
|
currentShaderProgram->Unbind();
|
2022-05-02 13:57:22 -07:00
|
|
|
|
|
|
|
|
m_ShaderProgram = nextShaderProgram;
|
2022-03-14 15:16:14 -07:00
|
|
|
}
|
|
|
|
|
|
2022-12-31 10:29:44 -08:00
|
|
|
const SDepthStencilStateDesc& currentDepthStencilStateDesc = m_GraphicsPipelineStateDesc.depthStencilState;
|
|
|
|
|
const SDepthStencilStateDesc& nextDepthStencilStateDesc = pipelineStateDesc.depthStencilState;
|
2022-02-01 08:38:55 -08:00
|
|
|
if (force || currentDepthStencilStateDesc.depthTestEnabled != nextDepthStencilStateDesc.depthTestEnabled)
|
|
|
|
|
{
|
|
|
|
|
if (nextDepthStencilStateDesc.depthTestEnabled)
|
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
|
else
|
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
|
}
|
2022-01-31 12:10:06 -08:00
|
|
|
if (force || currentDepthStencilStateDesc.depthCompareOp != nextDepthStencilStateDesc.depthCompareOp)
|
|
|
|
|
{
|
2022-02-01 08:38:55 -08:00
|
|
|
glDepthFunc(Mapping::FromCompareOp(nextDepthStencilStateDesc.depthCompareOp));
|
2022-01-31 12:10:06 -08:00
|
|
|
}
|
|
|
|
|
if (force || currentDepthStencilStateDesc.depthWriteEnabled != nextDepthStencilStateDesc.depthWriteEnabled)
|
|
|
|
|
{
|
2022-02-05 08:59:23 -08:00
|
|
|
ApplyDepthMask(nextDepthStencilStateDesc.depthWriteEnabled);
|
2022-01-31 12:10:06 -08:00
|
|
|
}
|
|
|
|
|
|
2022-02-01 08:38:55 -08:00
|
|
|
if (force || currentDepthStencilStateDesc.stencilTestEnabled != nextDepthStencilStateDesc.stencilTestEnabled)
|
|
|
|
|
{
|
|
|
|
|
if (nextDepthStencilStateDesc.stencilTestEnabled)
|
|
|
|
|
glEnable(GL_STENCIL_TEST);
|
|
|
|
|
else
|
|
|
|
|
glDisable(GL_STENCIL_TEST);
|
|
|
|
|
}
|
|
|
|
|
if (force ||
|
|
|
|
|
currentDepthStencilStateDesc.stencilFrontFace != nextDepthStencilStateDesc.stencilFrontFace ||
|
|
|
|
|
currentDepthStencilStateDesc.stencilBackFace != nextDepthStencilStateDesc.stencilBackFace)
|
|
|
|
|
{
|
|
|
|
|
if (nextDepthStencilStateDesc.stencilFrontFace == nextDepthStencilStateDesc.stencilBackFace)
|
|
|
|
|
{
|
|
|
|
|
glStencilOp(
|
|
|
|
|
Mapping::FromStencilOp(nextDepthStencilStateDesc.stencilFrontFace.failOp),
|
|
|
|
|
Mapping::FromStencilOp(nextDepthStencilStateDesc.stencilFrontFace.depthFailOp),
|
|
|
|
|
Mapping::FromStencilOp(nextDepthStencilStateDesc.stencilFrontFace.passOp));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (force || currentDepthStencilStateDesc.stencilFrontFace != nextDepthStencilStateDesc.stencilFrontFace)
|
|
|
|
|
{
|
|
|
|
|
glStencilOpSeparate(
|
|
|
|
|
GL_FRONT,
|
|
|
|
|
Mapping::FromStencilOp(nextDepthStencilStateDesc.stencilFrontFace.failOp),
|
|
|
|
|
Mapping::FromStencilOp(nextDepthStencilStateDesc.stencilFrontFace.depthFailOp),
|
|
|
|
|
Mapping::FromStencilOp(nextDepthStencilStateDesc.stencilFrontFace.passOp));
|
|
|
|
|
}
|
|
|
|
|
if (force || currentDepthStencilStateDesc.stencilBackFace != nextDepthStencilStateDesc.stencilBackFace)
|
|
|
|
|
{
|
|
|
|
|
glStencilOpSeparate(
|
|
|
|
|
GL_BACK,
|
|
|
|
|
Mapping::FromStencilOp(nextDepthStencilStateDesc.stencilBackFace.failOp),
|
|
|
|
|
Mapping::FromStencilOp(nextDepthStencilStateDesc.stencilBackFace.depthFailOp),
|
|
|
|
|
Mapping::FromStencilOp(nextDepthStencilStateDesc.stencilBackFace.passOp));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (force || currentDepthStencilStateDesc.stencilWriteMask != nextDepthStencilStateDesc.stencilWriteMask)
|
|
|
|
|
{
|
2022-02-05 08:59:23 -08:00
|
|
|
ApplyStencilMask(nextDepthStencilStateDesc.stencilWriteMask);
|
2022-02-01 08:38:55 -08:00
|
|
|
}
|
|
|
|
|
if (force ||
|
|
|
|
|
currentDepthStencilStateDesc.stencilReference != nextDepthStencilStateDesc.stencilReference ||
|
|
|
|
|
currentDepthStencilStateDesc.stencilReadMask != nextDepthStencilStateDesc.stencilReadMask ||
|
|
|
|
|
currentDepthStencilStateDesc.stencilFrontFace.compareOp != nextDepthStencilStateDesc.stencilFrontFace.compareOp ||
|
|
|
|
|
currentDepthStencilStateDesc.stencilBackFace.compareOp != nextDepthStencilStateDesc.stencilBackFace.compareOp)
|
|
|
|
|
{
|
|
|
|
|
if (nextDepthStencilStateDesc.stencilFrontFace.compareOp == nextDepthStencilStateDesc.stencilBackFace.compareOp)
|
|
|
|
|
{
|
|
|
|
|
glStencilFunc(
|
|
|
|
|
Mapping::FromCompareOp(nextDepthStencilStateDesc.stencilFrontFace.compareOp),
|
|
|
|
|
nextDepthStencilStateDesc.stencilReference,
|
|
|
|
|
nextDepthStencilStateDesc.stencilReadMask);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
glStencilFuncSeparate(GL_FRONT,
|
|
|
|
|
Mapping::FromCompareOp(nextDepthStencilStateDesc.stencilFrontFace.compareOp),
|
|
|
|
|
nextDepthStencilStateDesc.stencilReference,
|
|
|
|
|
nextDepthStencilStateDesc.stencilReadMask);
|
|
|
|
|
glStencilFuncSeparate(GL_BACK,
|
|
|
|
|
Mapping::FromCompareOp(nextDepthStencilStateDesc.stencilBackFace.compareOp),
|
|
|
|
|
nextDepthStencilStateDesc.stencilReference,
|
|
|
|
|
nextDepthStencilStateDesc.stencilReadMask);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-31 10:29:44 -08:00
|
|
|
const SBlendStateDesc& currentBlendStateDesc = m_GraphicsPipelineStateDesc.blendState;
|
|
|
|
|
const SBlendStateDesc& nextBlendStateDesc = pipelineStateDesc.blendState;
|
2022-01-19 09:28:47 -08:00
|
|
|
if (force || currentBlendStateDesc.enabled != nextBlendStateDesc.enabled)
|
|
|
|
|
{
|
|
|
|
|
if (nextBlendStateDesc.enabled)
|
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
|
else
|
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
|
}
|
|
|
|
|
if (force ||
|
|
|
|
|
currentBlendStateDesc.srcColorBlendFactor != nextBlendStateDesc.srcColorBlendFactor ||
|
|
|
|
|
currentBlendStateDesc.srcAlphaBlendFactor != nextBlendStateDesc.srcAlphaBlendFactor ||
|
|
|
|
|
currentBlendStateDesc.dstColorBlendFactor != nextBlendStateDesc.dstColorBlendFactor ||
|
|
|
|
|
currentBlendStateDesc.dstAlphaBlendFactor != nextBlendStateDesc.dstAlphaBlendFactor)
|
|
|
|
|
{
|
|
|
|
|
if (nextBlendStateDesc.srcColorBlendFactor == nextBlendStateDesc.srcAlphaBlendFactor &&
|
|
|
|
|
nextBlendStateDesc.dstColorBlendFactor == nextBlendStateDesc.dstAlphaBlendFactor)
|
|
|
|
|
{
|
|
|
|
|
glBlendFunc(
|
|
|
|
|
Mapping::FromBlendFactor(nextBlendStateDesc.srcColorBlendFactor),
|
|
|
|
|
Mapping::FromBlendFactor(nextBlendStateDesc.dstColorBlendFactor));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
glBlendFuncSeparate(
|
|
|
|
|
Mapping::FromBlendFactor(nextBlendStateDesc.srcColorBlendFactor),
|
|
|
|
|
Mapping::FromBlendFactor(nextBlendStateDesc.dstColorBlendFactor),
|
|
|
|
|
Mapping::FromBlendFactor(nextBlendStateDesc.srcAlphaBlendFactor),
|
|
|
|
|
Mapping::FromBlendFactor(nextBlendStateDesc.dstAlphaBlendFactor));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (force ||
|
|
|
|
|
currentBlendStateDesc.colorBlendOp != nextBlendStateDesc.colorBlendOp ||
|
|
|
|
|
currentBlendStateDesc.alphaBlendOp != nextBlendStateDesc.alphaBlendOp)
|
|
|
|
|
{
|
|
|
|
|
if (nextBlendStateDesc.colorBlendOp == nextBlendStateDesc.alphaBlendOp)
|
|
|
|
|
{
|
|
|
|
|
glBlendEquation(Mapping::FromBlendOp(nextBlendStateDesc.colorBlendOp));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
glBlendEquationSeparate(
|
|
|
|
|
Mapping::FromBlendOp(nextBlendStateDesc.colorBlendOp),
|
|
|
|
|
Mapping::FromBlendOp(nextBlendStateDesc.alphaBlendOp));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (force ||
|
|
|
|
|
currentBlendStateDesc.constant != nextBlendStateDesc.constant)
|
|
|
|
|
{
|
|
|
|
|
glBlendColor(
|
|
|
|
|
nextBlendStateDesc.constant.r,
|
|
|
|
|
nextBlendStateDesc.constant.g,
|
|
|
|
|
nextBlendStateDesc.constant.b,
|
|
|
|
|
nextBlendStateDesc.constant.a);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-31 12:10:06 -08:00
|
|
|
if (force ||
|
|
|
|
|
currentBlendStateDesc.colorWriteMask != nextBlendStateDesc.colorWriteMask)
|
|
|
|
|
{
|
2022-02-05 08:59:23 -08:00
|
|
|
ApplyColorMask(nextBlendStateDesc.colorWriteMask);
|
2022-01-31 12:10:06 -08:00
|
|
|
}
|
|
|
|
|
|
2022-12-31 10:29:44 -08:00
|
|
|
const SRasterizationStateDesc& currentRasterizationStateDesc =
|
2022-02-25 14:05:06 -08:00
|
|
|
m_GraphicsPipelineStateDesc.rasterizationState;
|
2022-12-31 10:29:44 -08:00
|
|
|
const SRasterizationStateDesc& nextRasterizationStateDesc =
|
2022-02-25 14:05:06 -08:00
|
|
|
pipelineStateDesc.rasterizationState;
|
|
|
|
|
if (force ||
|
|
|
|
|
currentRasterizationStateDesc.polygonMode != nextRasterizationStateDesc.polygonMode)
|
|
|
|
|
{
|
|
|
|
|
#if !CONFIG2_GLES
|
|
|
|
|
glPolygonMode(
|
|
|
|
|
GL_FRONT_AND_BACK,
|
|
|
|
|
nextRasterizationStateDesc.polygonMode == PolygonMode::LINE ? GL_LINE : GL_FILL);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-27 09:25:37 -08:00
|
|
|
if (force ||
|
|
|
|
|
currentRasterizationStateDesc.cullMode != nextRasterizationStateDesc.cullMode)
|
|
|
|
|
{
|
|
|
|
|
if (nextRasterizationStateDesc.cullMode == CullMode::NONE)
|
|
|
|
|
{
|
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (force || currentRasterizationStateDesc.cullMode == CullMode::NONE)
|
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
|
glCullFace(nextRasterizationStateDesc.cullMode == CullMode::FRONT ? GL_FRONT : GL_BACK);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (force ||
|
|
|
|
|
currentRasterizationStateDesc.frontFace != nextRasterizationStateDesc.frontFace)
|
|
|
|
|
{
|
|
|
|
|
if (nextRasterizationStateDesc.frontFace == FrontFace::CLOCKWISE)
|
|
|
|
|
glFrontFace(GL_CW);
|
|
|
|
|
else
|
|
|
|
|
glFrontFace(GL_CCW);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-26 13:49:32 -08:00
|
|
|
#if !CONFIG2_GLES
|
|
|
|
|
if (force ||
|
|
|
|
|
currentRasterizationStateDesc.depthBiasEnabled != nextRasterizationStateDesc.depthBiasEnabled)
|
|
|
|
|
{
|
|
|
|
|
if (nextRasterizationStateDesc.depthBiasEnabled)
|
|
|
|
|
glEnable(GL_POLYGON_OFFSET_FILL);
|
|
|
|
|
else
|
|
|
|
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
|
|
|
|
}
|
|
|
|
|
if (force ||
|
|
|
|
|
currentRasterizationStateDesc.depthBiasConstantFactor != nextRasterizationStateDesc.depthBiasConstantFactor ||
|
|
|
|
|
currentRasterizationStateDesc.depthBiasSlopeFactor != nextRasterizationStateDesc.depthBiasSlopeFactor)
|
|
|
|
|
{
|
|
|
|
|
glPolygonOffset(
|
|
|
|
|
nextRasterizationStateDesc.depthBiasSlopeFactor,
|
|
|
|
|
nextRasterizationStateDesc.depthBiasConstantFactor);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-05-02 14:52:21 -07:00
|
|
|
ogl_WarnIfError();
|
|
|
|
|
|
2022-01-19 09:28:47 -08:00
|
|
|
m_GraphicsPipelineStateDesc = pipelineStateDesc;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-05 08:59:23 -08:00
|
|
|
void CDeviceCommandContext::BlitFramebuffer(
|
2023-02-17 09:36:10 -08:00
|
|
|
IFramebuffer* srcFramebuffer, IFramebuffer* dstFramebuffer,
|
2025-05-21 03:29:47 -07:00
|
|
|
[[maybe_unused]] const Rect& sourceRegion, [[maybe_unused]] const Rect& destinationRegion,
|
|
|
|
|
[[maybe_unused]] const Sampler::Filter filter)
|
2022-02-05 08:59:23 -08:00
|
|
|
{
|
2022-10-12 14:51:27 -07:00
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
2025-05-21 03:29:47 -07:00
|
|
|
[[maybe_unused]] CFramebuffer* destinationFramebuffer = dstFramebuffer->As<CFramebuffer>();
|
|
|
|
|
[[maybe_unused]] CFramebuffer* sourceFramebuffer = srcFramebuffer->As<CFramebuffer>();
|
2022-02-05 08:59:23 -08:00
|
|
|
#if CONFIG2_GLES
|
|
|
|
|
debug_warn("CDeviceCommandContext::BlitFramebuffer is not implemented for GLES");
|
|
|
|
|
#else
|
|
|
|
|
// Source framebuffer should not be backbuffer.
|
2022-05-08 15:02:46 -07:00
|
|
|
ENSURE(sourceFramebuffer->GetHandle() != 0);
|
|
|
|
|
ENSURE(destinationFramebuffer != sourceFramebuffer);
|
2022-02-05 08:59:23 -08:00
|
|
|
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, sourceFramebuffer->GetHandle());
|
|
|
|
|
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, destinationFramebuffer->GetHandle());
|
2023-02-17 09:36:10 -08:00
|
|
|
// TODO: add more check for internal formats.
|
|
|
|
|
glBlitFramebufferEXT(
|
|
|
|
|
sourceRegion.x, sourceRegion.y, sourceRegion.width, sourceRegion.height,
|
|
|
|
|
destinationRegion.x, destinationRegion.y, destinationRegion.width, destinationRegion.height,
|
|
|
|
|
(sourceFramebuffer->GetAttachmentMask() & destinationFramebuffer->GetAttachmentMask()),
|
|
|
|
|
filter == Sampler::Filter::LINEAR ? GL_LINEAR : GL_NEAREST);
|
|
|
|
|
ogl_WarnIfError();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::ResolveFramebuffer(
|
|
|
|
|
IFramebuffer* srcFramebuffer, IFramebuffer* dstFramebuffer)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
|
|
|
|
CFramebuffer* destinationFramebuffer = dstFramebuffer->As<CFramebuffer>();
|
|
|
|
|
CFramebuffer* sourceFramebuffer = srcFramebuffer->As<CFramebuffer>();
|
|
|
|
|
ENSURE(destinationFramebuffer->GetWidth() == sourceFramebuffer->GetWidth());
|
|
|
|
|
ENSURE(destinationFramebuffer->GetHeight() == sourceFramebuffer->GetHeight());
|
|
|
|
|
#if CONFIG2_GLES
|
|
|
|
|
debug_warn("CDeviceCommandContext::ResolveFramebuffer is not implemented for GLES");
|
|
|
|
|
#else
|
|
|
|
|
// Source framebuffer should not be backbuffer.
|
|
|
|
|
ENSURE(sourceFramebuffer->GetHandle() != 0);
|
|
|
|
|
ENSURE(destinationFramebuffer != sourceFramebuffer);
|
|
|
|
|
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, sourceFramebuffer->GetHandle());
|
|
|
|
|
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, destinationFramebuffer->GetHandle());
|
2022-02-05 08:59:23 -08:00
|
|
|
glBlitFramebufferEXT(
|
|
|
|
|
0, 0, sourceFramebuffer->GetWidth(), sourceFramebuffer->GetHeight(),
|
|
|
|
|
0, 0, sourceFramebuffer->GetWidth(), sourceFramebuffer->GetHeight(),
|
|
|
|
|
(sourceFramebuffer->GetAttachmentMask() & destinationFramebuffer->GetAttachmentMask()),
|
|
|
|
|
GL_NEAREST);
|
2022-05-02 14:52:21 -07:00
|
|
|
ogl_WarnIfError();
|
2022-02-05 08:59:23 -08:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::ClearFramebuffer(const bool color, const bool depth, const bool stencil)
|
|
|
|
|
{
|
2022-11-26 13:55:17 -08:00
|
|
|
ENSURE(m_InsideFramebufferPass);
|
2022-02-05 08:59:23 -08:00
|
|
|
const bool needsColor = color && (m_Framebuffer->GetAttachmentMask() & GL_COLOR_BUFFER_BIT) != 0;
|
|
|
|
|
const bool needsDepth = depth && (m_Framebuffer->GetAttachmentMask() & GL_DEPTH_BUFFER_BIT) != 0;
|
|
|
|
|
const bool needsStencil = stencil && (m_Framebuffer->GetAttachmentMask() & GL_STENCIL_BUFFER_BIT) != 0;
|
|
|
|
|
GLbitfield mask = 0;
|
|
|
|
|
if (needsColor)
|
|
|
|
|
{
|
|
|
|
|
ApplyColorMask(ColorWriteMask::RED | ColorWriteMask::GREEN | ColorWriteMask::BLUE | ColorWriteMask::ALPHA);
|
|
|
|
|
glClearColor(
|
|
|
|
|
m_Framebuffer->GetClearColor().r,
|
|
|
|
|
m_Framebuffer->GetClearColor().g,
|
|
|
|
|
m_Framebuffer->GetClearColor().b,
|
|
|
|
|
m_Framebuffer->GetClearColor().a);
|
|
|
|
|
mask |= GL_COLOR_BUFFER_BIT;
|
|
|
|
|
}
|
|
|
|
|
if (needsDepth)
|
|
|
|
|
{
|
|
|
|
|
ApplyDepthMask(true);
|
|
|
|
|
mask |= GL_DEPTH_BUFFER_BIT;
|
|
|
|
|
}
|
|
|
|
|
if (needsStencil)
|
|
|
|
|
{
|
|
|
|
|
ApplyStencilMask(std::numeric_limits<uint32_t>::max());
|
|
|
|
|
mask |= GL_STENCIL_BUFFER_BIT;
|
|
|
|
|
}
|
|
|
|
|
glClear(mask);
|
2022-04-23 13:11:14 -07:00
|
|
|
ogl_WarnIfError();
|
2022-02-05 08:59:23 -08:00
|
|
|
if (needsColor)
|
|
|
|
|
ApplyColorMask(m_GraphicsPipelineStateDesc.blendState.colorWriteMask);
|
|
|
|
|
if (needsDepth)
|
|
|
|
|
ApplyDepthMask(m_GraphicsPipelineStateDesc.depthStencilState.depthWriteEnabled);
|
|
|
|
|
if (needsStencil)
|
|
|
|
|
ApplyStencilMask(m_GraphicsPipelineStateDesc.depthStencilState.stencilWriteMask);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-12 14:51:27 -07:00
|
|
|
void CDeviceCommandContext::BeginFramebufferPass(IFramebuffer* framebuffer)
|
2022-02-05 08:59:23 -08:00
|
|
|
{
|
2022-12-31 10:29:44 -08:00
|
|
|
SetGraphicsPipelineStateImpl(
|
|
|
|
|
MakeDefaultGraphicsPipelineStateDesc(), false);
|
|
|
|
|
|
2022-10-12 14:51:27 -07:00
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
|
|
|
|
m_InsideFramebufferPass = true;
|
2022-02-05 08:59:23 -08:00
|
|
|
ENSURE(framebuffer);
|
2022-05-08 15:02:46 -07:00
|
|
|
m_Framebuffer = framebuffer->As<CFramebuffer>();
|
|
|
|
|
ENSURE(m_Framebuffer->GetHandle() == 0 || (m_Framebuffer->GetWidth() > 0 && m_Framebuffer->GetHeight() > 0));
|
|
|
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_Framebuffer->GetHandle());
|
2022-05-02 14:52:21 -07:00
|
|
|
ogl_WarnIfError();
|
2022-11-26 13:55:17 -08:00
|
|
|
if (m_Device->UseFramebufferInvalidating())
|
|
|
|
|
{
|
|
|
|
|
InvalidateFramebuffer(
|
|
|
|
|
m_Framebuffer,
|
|
|
|
|
m_Framebuffer->GetColorAttachmentLoadOp() != AttachmentLoadOp::LOAD,
|
|
|
|
|
m_Framebuffer->GetDepthStencilAttachmentLoadOp() != AttachmentLoadOp::LOAD);
|
|
|
|
|
}
|
|
|
|
|
const bool needsClearColor =
|
|
|
|
|
m_Framebuffer->GetColorAttachmentLoadOp() == AttachmentLoadOp::CLEAR;
|
|
|
|
|
const bool needsClearDepthStencil =
|
|
|
|
|
m_Framebuffer->GetDepthStencilAttachmentLoadOp() == AttachmentLoadOp::CLEAR;
|
|
|
|
|
if (needsClearColor || needsClearDepthStencil)
|
|
|
|
|
{
|
|
|
|
|
ClearFramebuffer(
|
|
|
|
|
needsClearColor, needsClearDepthStencil, needsClearDepthStencil);
|
|
|
|
|
}
|
2022-02-05 08:59:23 -08:00
|
|
|
}
|
|
|
|
|
|
2022-10-12 14:51:27 -07:00
|
|
|
void CDeviceCommandContext::EndFramebufferPass()
|
|
|
|
|
{
|
2022-11-26 13:55:17 -08:00
|
|
|
if (m_Device->UseFramebufferInvalidating())
|
|
|
|
|
{
|
|
|
|
|
InvalidateFramebuffer(
|
|
|
|
|
m_Framebuffer,
|
|
|
|
|
m_Framebuffer->GetColorAttachmentStoreOp() != AttachmentStoreOp::STORE,
|
|
|
|
|
m_Framebuffer->GetDepthStencilAttachmentStoreOp() != AttachmentStoreOp::STORE);
|
|
|
|
|
}
|
2022-10-12 14:51:27 -07:00
|
|
|
ENSURE(m_InsideFramebufferPass);
|
|
|
|
|
m_InsideFramebufferPass = false;
|
2022-11-26 13:55:17 -08:00
|
|
|
CFramebuffer* framebuffer = m_Device->GetCurrentBackbuffer(
|
|
|
|
|
Renderer::Backend::AttachmentLoadOp::DONT_CARE,
|
|
|
|
|
Renderer::Backend::AttachmentStoreOp::DONT_CARE,
|
|
|
|
|
Renderer::Backend::AttachmentLoadOp::DONT_CARE,
|
|
|
|
|
Renderer::Backend::AttachmentStoreOp::DONT_CARE)->As<CFramebuffer>();
|
|
|
|
|
if (framebuffer->GetHandle() != m_Framebuffer->GetHandle())
|
|
|
|
|
{
|
|
|
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer->GetHandle());
|
|
|
|
|
ogl_WarnIfError();
|
|
|
|
|
}
|
|
|
|
|
m_Framebuffer = framebuffer;
|
2024-01-17 11:40:27 -08:00
|
|
|
|
|
|
|
|
SetGraphicsPipelineStateImpl(MakeDefaultGraphicsPipelineStateDesc(), false);
|
2022-10-12 14:51:27 -07:00
|
|
|
}
|
|
|
|
|
|
2022-04-17 07:55:00 -07:00
|
|
|
void CDeviceCommandContext::ReadbackFramebufferSync(
|
|
|
|
|
const uint32_t x, const uint32_t y, const uint32_t width, const uint32_t height,
|
|
|
|
|
void* data)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_Framebuffer);
|
|
|
|
|
glReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, data);
|
|
|
|
|
ogl_WarnIfError();
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-26 15:17:48 -08:00
|
|
|
void CDeviceCommandContext::SetScissors(const uint32_t scissorCount, const Rect* scissors)
|
2022-02-01 09:58:21 -08:00
|
|
|
{
|
|
|
|
|
ENSURE(scissorCount <= 1);
|
|
|
|
|
if (scissorCount == 0)
|
|
|
|
|
{
|
|
|
|
|
if (m_ScissorCount != scissorCount)
|
|
|
|
|
glDisable(GL_SCISSOR_TEST);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (m_ScissorCount != scissorCount)
|
|
|
|
|
glEnable(GL_SCISSOR_TEST);
|
2022-02-05 08:59:23 -08:00
|
|
|
ENSURE(scissors);
|
2022-02-01 09:58:21 -08:00
|
|
|
if (m_ScissorCount != scissorCount || m_Scissors[0] != scissors[0])
|
|
|
|
|
{
|
|
|
|
|
m_Scissors[0] = scissors[0];
|
|
|
|
|
glScissor(m_Scissors[0].x, m_Scissors[0].y, m_Scissors[0].width, m_Scissors[0].height);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-05-02 14:52:21 -07:00
|
|
|
ogl_WarnIfError();
|
2022-02-01 09:58:21 -08:00
|
|
|
m_ScissorCount = scissorCount;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-26 15:17:48 -08:00
|
|
|
void CDeviceCommandContext::SetViewports(const uint32_t viewportCount, const Rect* viewports)
|
|
|
|
|
{
|
2022-12-28 22:53:06 -08:00
|
|
|
ENSURE(m_InsideFramebufferPass);
|
2022-02-26 15:17:48 -08:00
|
|
|
ENSURE(viewportCount == 1);
|
|
|
|
|
glViewport(viewports[0].x, viewports[0].y, viewports[0].width, viewports[0].height);
|
2022-05-02 14:52:21 -07:00
|
|
|
ogl_WarnIfError();
|
2022-02-26 15:17:48 -08:00
|
|
|
}
|
|
|
|
|
|
2023-01-05 16:39:25 -08:00
|
|
|
void CDeviceCommandContext::SetVertexInputLayout(
|
|
|
|
|
IVertexInputLayout* vertexInputLayout)
|
2022-04-23 13:11:14 -07:00
|
|
|
{
|
2023-01-05 16:39:25 -08:00
|
|
|
ENSURE(vertexInputLayout);
|
|
|
|
|
for (const SVertexAttributeFormat& attribute : vertexInputLayout->As<CVertexInputLayout>()->GetAttributes())
|
|
|
|
|
{
|
|
|
|
|
const uint32_t index = static_cast<uint32_t>(attribute.stream);
|
|
|
|
|
ENSURE(index < m_VertexAttributeFormat.size());
|
|
|
|
|
ENSURE(attribute.bindingSlot < m_VertexAttributeFormat.size());
|
|
|
|
|
if (!m_VertexAttributeFormat[index].active)
|
|
|
|
|
continue;
|
2022-04-23 13:11:14 -07:00
|
|
|
|
2023-01-05 16:39:25 -08:00
|
|
|
m_VertexAttributeFormat[index].format = attribute.format;
|
|
|
|
|
m_VertexAttributeFormat[index].offset = attribute.offset;
|
|
|
|
|
m_VertexAttributeFormat[index].stride = attribute.stride;
|
|
|
|
|
m_VertexAttributeFormat[index].rate = attribute.rate;
|
|
|
|
|
m_VertexAttributeFormat[index].bindingSlot = attribute.bindingSlot;
|
|
|
|
|
|
|
|
|
|
m_VertexAttributeFormat[index].initialized = true;
|
|
|
|
|
}
|
2022-04-23 13:11:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetVertexBuffer(
|
2022-10-09 04:47:34 -07:00
|
|
|
const uint32_t bindingSlot, IBuffer* buffer, const uint32_t offset)
|
2022-04-23 13:11:14 -07:00
|
|
|
{
|
|
|
|
|
ENSURE(buffer);
|
2022-05-08 15:02:46 -07:00
|
|
|
ENSURE(buffer->GetType() == IBuffer::Type::VERTEX);
|
2022-05-02 13:57:22 -07:00
|
|
|
ENSURE(m_ShaderProgram);
|
2022-05-08 15:02:46 -07:00
|
|
|
BindBuffer(buffer->GetType(), buffer->As<CBuffer>());
|
2022-04-23 13:11:14 -07:00
|
|
|
for (size_t index = 0; index < m_VertexAttributeFormat.size(); ++index)
|
|
|
|
|
{
|
|
|
|
|
if (!m_VertexAttributeFormat[index].active || m_VertexAttributeFormat[index].bindingSlot != bindingSlot)
|
|
|
|
|
continue;
|
|
|
|
|
ENSURE(m_VertexAttributeFormat[index].initialized);
|
|
|
|
|
const VertexAttributeStream stream = static_cast<VertexAttributeStream>(index);
|
2022-05-02 13:57:22 -07:00
|
|
|
m_ShaderProgram->VertexAttribPointer(stream,
|
2022-04-23 13:11:14 -07:00
|
|
|
m_VertexAttributeFormat[index].format,
|
2022-10-09 04:47:34 -07:00
|
|
|
m_VertexAttributeFormat[index].offset + offset,
|
2022-04-23 13:11:14 -07:00
|
|
|
m_VertexAttributeFormat[index].stride,
|
2022-05-24 00:11:23 -07:00
|
|
|
m_VertexAttributeFormat[index].rate,
|
2022-04-23 13:11:14 -07:00
|
|
|
nullptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetVertexBufferData(
|
2022-05-24 00:11:23 -07:00
|
|
|
const uint32_t bindingSlot, const void* data, const uint32_t dataSize)
|
2022-04-23 13:11:14 -07:00
|
|
|
{
|
|
|
|
|
ENSURE(data);
|
2022-05-02 13:57:22 -07:00
|
|
|
ENSURE(m_ShaderProgram);
|
2022-05-24 00:11:23 -07:00
|
|
|
ENSURE(dataSize > 0);
|
2022-04-23 13:11:14 -07:00
|
|
|
BindBuffer(CBuffer::Type::VERTEX, nullptr);
|
|
|
|
|
for (size_t index = 0; index < m_VertexAttributeFormat.size(); ++index)
|
|
|
|
|
{
|
|
|
|
|
if (!m_VertexAttributeFormat[index].active || m_VertexAttributeFormat[index].bindingSlot != bindingSlot)
|
|
|
|
|
continue;
|
|
|
|
|
ENSURE(m_VertexAttributeFormat[index].initialized);
|
|
|
|
|
const VertexAttributeStream stream = static_cast<VertexAttributeStream>(index);
|
2022-05-24 00:11:23 -07:00
|
|
|
// We don't know how many vertices will be used in a draw command, so we
|
|
|
|
|
// assume at least one vertex.
|
|
|
|
|
ENSURE(dataSize >= m_VertexAttributeFormat[index].offset + m_VertexAttributeFormat[index].stride);
|
2022-05-02 13:57:22 -07:00
|
|
|
m_ShaderProgram->VertexAttribPointer(stream,
|
2022-04-23 13:11:14 -07:00
|
|
|
m_VertexAttributeFormat[index].format,
|
|
|
|
|
m_VertexAttributeFormat[index].offset,
|
|
|
|
|
m_VertexAttributeFormat[index].stride,
|
2022-05-24 00:11:23 -07:00
|
|
|
m_VertexAttributeFormat[index].rate,
|
2022-04-23 13:11:14 -07:00
|
|
|
data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-08 15:02:46 -07:00
|
|
|
void CDeviceCommandContext::SetIndexBuffer(IBuffer* buffer)
|
2022-03-02 23:09:59 -08:00
|
|
|
{
|
|
|
|
|
ENSURE(buffer->GetType() == CBuffer::Type::INDEX);
|
2022-05-08 15:02:46 -07:00
|
|
|
m_IndexBuffer = buffer->As<CBuffer>();
|
2022-03-02 23:09:59 -08:00
|
|
|
m_IndexBufferData = nullptr;
|
|
|
|
|
BindBuffer(CBuffer::Type::INDEX, m_IndexBuffer);
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-24 00:11:23 -07:00
|
|
|
void CDeviceCommandContext::SetIndexBufferData(const void* data, const uint32_t dataSize)
|
2022-03-02 23:09:59 -08:00
|
|
|
{
|
2022-05-24 00:11:23 -07:00
|
|
|
ENSURE(dataSize > 0);
|
2022-03-02 23:09:59 -08:00
|
|
|
if (m_IndexBuffer)
|
|
|
|
|
{
|
|
|
|
|
BindBuffer(CBuffer::Type::INDEX, nullptr);
|
|
|
|
|
m_IndexBuffer = nullptr;
|
|
|
|
|
}
|
|
|
|
|
m_IndexBufferData = data;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-14 15:16:14 -07:00
|
|
|
void CDeviceCommandContext::BeginPass()
|
|
|
|
|
{
|
|
|
|
|
ENSURE(!m_InsidePass);
|
|
|
|
|
m_InsidePass = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::EndPass()
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_InsidePass);
|
|
|
|
|
m_InsidePass = false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-02 23:09:59 -08:00
|
|
|
void CDeviceCommandContext::Draw(
|
|
|
|
|
const uint32_t firstVertex, const uint32_t vertexCount)
|
|
|
|
|
{
|
2022-05-02 13:57:22 -07:00
|
|
|
ENSURE(m_ShaderProgram);
|
2022-03-14 15:16:14 -07:00
|
|
|
ENSURE(m_InsidePass);
|
2022-03-02 23:09:59 -08:00
|
|
|
// Some drivers apparently don't like count = 0 in glDrawArrays here, so skip
|
|
|
|
|
// all drawing in that case.
|
|
|
|
|
if (vertexCount == 0)
|
|
|
|
|
return;
|
2022-05-02 13:57:22 -07:00
|
|
|
m_ShaderProgram->AssertPointersBound();
|
2022-03-02 23:09:59 -08:00
|
|
|
glDrawArrays(GL_TRIANGLES, firstVertex, vertexCount);
|
2022-04-23 13:11:14 -07:00
|
|
|
ogl_WarnIfError();
|
2022-03-02 23:09:59 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::DrawIndexed(
|
|
|
|
|
const uint32_t firstIndex, const uint32_t indexCount, const int32_t vertexOffset)
|
|
|
|
|
{
|
2022-05-02 13:57:22 -07:00
|
|
|
ENSURE(m_ShaderProgram);
|
2022-03-14 15:16:14 -07:00
|
|
|
ENSURE(m_InsidePass);
|
2022-03-02 23:09:59 -08:00
|
|
|
if (indexCount == 0)
|
|
|
|
|
return;
|
|
|
|
|
ENSURE(m_IndexBuffer || m_IndexBufferData);
|
|
|
|
|
ENSURE(vertexOffset == 0);
|
|
|
|
|
if (m_IndexBuffer)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(sizeof(uint16_t) * (firstIndex + indexCount) <= m_IndexBuffer->GetSize());
|
|
|
|
|
}
|
2022-05-02 13:57:22 -07:00
|
|
|
m_ShaderProgram->AssertPointersBound();
|
2022-03-02 23:09:59 -08:00
|
|
|
// Don't use glMultiDrawElements here since it doesn't have a significant
|
|
|
|
|
// performance impact and it suffers from various driver bugs (e.g. it breaks
|
|
|
|
|
// in Mesa 7.10 swrast with index VBOs).
|
|
|
|
|
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT,
|
|
|
|
|
static_cast<const void*>((static_cast<const uint8_t*>(m_IndexBufferData) + sizeof(uint16_t) * firstIndex)));
|
2022-04-23 13:11:14 -07:00
|
|
|
ogl_WarnIfError();
|
2022-03-02 23:09:59 -08:00
|
|
|
}
|
|
|
|
|
|
2022-05-24 00:11:23 -07:00
|
|
|
void CDeviceCommandContext::DrawInstanced(
|
2025-05-21 03:29:47 -07:00
|
|
|
[[maybe_unused]] const uint32_t firstVertex, const uint32_t vertexCount,
|
2022-05-24 00:11:23 -07:00
|
|
|
const uint32_t firstInstance, const uint32_t instanceCount)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_Device->GetCapabilities().instancing);
|
|
|
|
|
ENSURE(m_ShaderProgram);
|
|
|
|
|
ENSURE(m_InsidePass);
|
|
|
|
|
if (vertexCount == 0 || instanceCount == 0)
|
|
|
|
|
return;
|
|
|
|
|
ENSURE(firstInstance == 0);
|
|
|
|
|
m_ShaderProgram->AssertPointersBound();
|
2022-05-26 09:36:57 -07:00
|
|
|
#if CONFIG2_GLES
|
|
|
|
|
ENSURE(!m_Device->GetCapabilities().instancing);
|
|
|
|
|
#else
|
2022-05-24 00:11:23 -07:00
|
|
|
glDrawArraysInstancedARB(GL_TRIANGLES, firstVertex, vertexCount, instanceCount);
|
2022-05-26 09:36:57 -07:00
|
|
|
#endif
|
2022-05-24 00:11:23 -07:00
|
|
|
ogl_WarnIfError();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::DrawIndexedInstanced(
|
|
|
|
|
const uint32_t firstIndex, const uint32_t indexCount,
|
2025-05-21 03:29:47 -07:00
|
|
|
const uint32_t firstInstance, [[maybe_unused]] const uint32_t instanceCount,
|
2022-05-24 00:11:23 -07:00
|
|
|
const int32_t vertexOffset)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_Device->GetCapabilities().instancing);
|
|
|
|
|
ENSURE(m_ShaderProgram);
|
|
|
|
|
ENSURE(m_InsidePass);
|
|
|
|
|
ENSURE(m_IndexBuffer || m_IndexBufferData);
|
|
|
|
|
if (indexCount == 0)
|
|
|
|
|
return;
|
|
|
|
|
ENSURE(firstInstance == 0 && vertexOffset == 0);
|
|
|
|
|
if (m_IndexBuffer)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(sizeof(uint16_t) * (firstIndex + indexCount) <= m_IndexBuffer->GetSize());
|
|
|
|
|
}
|
|
|
|
|
m_ShaderProgram->AssertPointersBound();
|
|
|
|
|
// Don't use glMultiDrawElements here since it doesn't have a significant
|
|
|
|
|
// performance impact and it suffers from various driver bugs (e.g. it breaks
|
|
|
|
|
// in Mesa 7.10 swrast with index VBOs).
|
2022-05-26 09:36:57 -07:00
|
|
|
#if CONFIG2_GLES
|
|
|
|
|
ENSURE(!m_Device->GetCapabilities().instancing);
|
|
|
|
|
#else
|
2022-05-24 00:11:23 -07:00
|
|
|
glDrawElementsInstancedARB(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT,
|
|
|
|
|
static_cast<const void*>((static_cast<const uint8_t*>(m_IndexBufferData) + sizeof(uint16_t) * firstIndex)),
|
|
|
|
|
instanceCount);
|
2022-05-26 09:36:57 -07:00
|
|
|
#endif
|
2022-05-24 00:11:23 -07:00
|
|
|
ogl_WarnIfError();
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-02 23:09:59 -08:00
|
|
|
void CDeviceCommandContext::DrawIndexedInRange(
|
|
|
|
|
const uint32_t firstIndex, const uint32_t indexCount,
|
2025-05-21 03:29:47 -07:00
|
|
|
[[maybe_unused]] const uint32_t start, [[maybe_unused]] const uint32_t end)
|
2022-03-02 23:09:59 -08:00
|
|
|
{
|
2022-05-02 13:57:22 -07:00
|
|
|
ENSURE(m_ShaderProgram);
|
2022-03-14 15:16:14 -07:00
|
|
|
ENSURE(m_InsidePass);
|
2022-03-02 23:09:59 -08:00
|
|
|
if (indexCount == 0)
|
|
|
|
|
return;
|
|
|
|
|
ENSURE(m_IndexBuffer || m_IndexBufferData);
|
|
|
|
|
const void* indices =
|
|
|
|
|
static_cast<const void*>((static_cast<const uint8_t*>(m_IndexBufferData) + sizeof(uint16_t) * firstIndex));
|
2022-05-02 13:57:22 -07:00
|
|
|
m_ShaderProgram->AssertPointersBound();
|
2022-03-02 23:09:59 -08:00
|
|
|
// Draw with DrawRangeElements where available, since it might be more
|
|
|
|
|
// efficient for slow hardware.
|
|
|
|
|
#if CONFIG2_GLES
|
|
|
|
|
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, indices);
|
|
|
|
|
#else
|
2026-04-25 08:47:06 -07:00
|
|
|
glDrawRangeElements(GL_TRIANGLES, start, end, indexCount, GL_UNSIGNED_SHORT, indices);
|
2022-03-02 23:09:59 -08:00
|
|
|
#endif
|
2022-04-23 13:11:14 -07:00
|
|
|
ogl_WarnIfError();
|
2022-03-02 23:09:59 -08:00
|
|
|
}
|
|
|
|
|
|
2024-01-17 11:40:27 -08:00
|
|
|
void CDeviceCommandContext::BeginComputePass()
|
|
|
|
|
{
|
|
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
|
|
|
|
ENSURE(!m_InsideComputePass);
|
|
|
|
|
m_InsideComputePass = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::EndComputePass()
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_InsideComputePass);
|
|
|
|
|
m_InsideComputePass = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::Dispatch(
|
2025-05-21 03:29:47 -07:00
|
|
|
[[maybe_unused]] const uint32_t groupCountX,
|
|
|
|
|
[[maybe_unused]] const uint32_t groupCountY,
|
|
|
|
|
[[maybe_unused]] const uint32_t groupCountZ)
|
2024-01-17 11:40:27 -08:00
|
|
|
{
|
2024-12-09 13:47:15 -08:00
|
|
|
#if !CONFIG2_GLES
|
2024-01-17 11:40:27 -08:00
|
|
|
ENSURE(m_InsideComputePass);
|
|
|
|
|
glDispatchCompute(groupCountX, groupCountY, groupCountZ);
|
2024-12-09 13:47:15 -08:00
|
|
|
// Storage buffers should be managed explicitly by InsertMemoryBarrier.
|
|
|
|
|
if (m_ShaderProgram->HasImageUniforms())
|
|
|
|
|
{
|
|
|
|
|
// TODO: we might want to do binding tracking to avoid redundant barriers.
|
|
|
|
|
glMemoryBarrier(
|
|
|
|
|
GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT | GL_TEXTURE_UPDATE_BARRIER_BIT | GL_FRAMEBUFFER_BARRIER_BIT );
|
|
|
|
|
}
|
2024-09-12 07:31:43 -07:00
|
|
|
#endif
|
2024-01-17 11:40:27 -08:00
|
|
|
}
|
|
|
|
|
|
2024-12-09 13:47:15 -08:00
|
|
|
void CDeviceCommandContext::InsertMemoryBarrier(
|
2025-05-21 03:29:47 -07:00
|
|
|
const uint32_t /*srcStageMask*/, [[maybe_unused]] const uint32_t dstStageMask,
|
|
|
|
|
[[maybe_unused]] const uint32_t srcAccessMask, [[maybe_unused]] const uint32_t dstAccessMask)
|
2024-12-09 13:47:15 -08:00
|
|
|
{
|
|
|
|
|
#if !CONFIG2_GLES
|
|
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
|
|
|
|
GLbitfield barriers{0};
|
|
|
|
|
if (srcAccessMask & Access::SHADER_WRITE)
|
|
|
|
|
{
|
|
|
|
|
if (dstStageMask & PipelineStage::VERTEX_INPUT)
|
|
|
|
|
{
|
|
|
|
|
if (dstAccessMask & Access::VERTEX_ATTRIBUTE_READ)
|
|
|
|
|
barriers |= GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT;
|
|
|
|
|
if (dstAccessMask & Access::INDEX_READ)
|
|
|
|
|
barriers |= GL_ELEMENT_ARRAY_BARRIER_BIT;
|
|
|
|
|
}
|
|
|
|
|
if (dstStageMask & (PipelineStage::VERTEX_SHADER | PipelineStage::FRAGMENT_SHADER | PipelineStage::COMPUTE_SHADER))
|
|
|
|
|
{
|
|
|
|
|
if (dstAccessMask & (Access::SHADER_READ | Access::SHADER_WRITE))
|
|
|
|
|
barriers |= GL_SHADER_STORAGE_BARRIER_BIT;
|
|
|
|
|
if (dstAccessMask & Access::UNIFORM_READ)
|
|
|
|
|
barriers |= GL_UNIFORM_BARRIER_BIT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (barriers)
|
|
|
|
|
glMemoryBarrier(barriers);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-08 15:02:46 -07:00
|
|
|
void CDeviceCommandContext::SetTexture(const int32_t bindingSlot, ITexture* texture)
|
2022-05-02 13:57:22 -07:00
|
|
|
{
|
|
|
|
|
ENSURE(m_ShaderProgram);
|
|
|
|
|
ENSURE(texture);
|
2022-10-12 16:19:27 -07:00
|
|
|
ENSURE(texture->GetUsage() & Renderer::Backend::ITexture::Usage::SAMPLED);
|
|
|
|
|
|
2022-05-02 13:57:22 -07:00
|
|
|
const CShaderProgram::TextureUnit textureUnit =
|
|
|
|
|
m_ShaderProgram->GetTextureUnit(bindingSlot);
|
|
|
|
|
if (!textureUnit.type)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (textureUnit.type != GL_SAMPLER_2D &&
|
|
|
|
|
#if !CONFIG2_GLES
|
|
|
|
|
textureUnit.type != GL_SAMPLER_2D_SHADOW &&
|
|
|
|
|
#endif
|
|
|
|
|
textureUnit.type != GL_SAMPLER_CUBE)
|
|
|
|
|
{
|
|
|
|
|
LOGERROR("CDeviceCommandContext::SetTexture: expected sampler at binding slot");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if !CONFIG2_GLES
|
|
|
|
|
if (textureUnit.type == GL_SAMPLER_2D_SHADOW)
|
|
|
|
|
{
|
2023-01-11 22:32:52 -08:00
|
|
|
if (!IsDepthFormat(texture->GetFormat()))
|
2022-05-02 13:57:22 -07:00
|
|
|
{
|
|
|
|
|
LOGERROR("CDeviceCommandContext::SetTexture: Invalid texture type (expected depth texture)");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
ENSURE(textureUnit.unit >= 0);
|
|
|
|
|
const uint32_t unit = textureUnit.unit;
|
|
|
|
|
if (unit >= m_BoundTextures.size())
|
|
|
|
|
{
|
|
|
|
|
LOGERROR("CDeviceCommandContext::SetTexture: Invalid texture unit (too big)");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-05-08 15:02:46 -07:00
|
|
|
BindTexture(unit, textureUnit.target, texture->As<CTexture>()->GetHandle());
|
2022-05-02 13:57:22 -07:00
|
|
|
}
|
|
|
|
|
|
2025-05-21 03:29:47 -07:00
|
|
|
void CDeviceCommandContext::SetStorageTexture([[maybe_unused]] const int32_t bindingSlot,
|
|
|
|
|
[[maybe_unused]] ITexture* texture)
|
2024-01-17 11:40:27 -08:00
|
|
|
{
|
2024-09-12 07:31:43 -07:00
|
|
|
#if !CONFIG2_GLES
|
2024-01-17 11:40:27 -08:00
|
|
|
ENSURE(m_ShaderProgram);
|
|
|
|
|
ENSURE(texture);
|
|
|
|
|
ENSURE(texture->GetUsage() & Renderer::Backend::ITexture::Usage::STORAGE);
|
|
|
|
|
|
|
|
|
|
const CShaderProgram::TextureUnit textureUnit =
|
|
|
|
|
m_ShaderProgram->GetTextureUnit(bindingSlot);
|
|
|
|
|
if (!textureUnit.type)
|
|
|
|
|
return;
|
|
|
|
|
ENSURE(textureUnit.type == GL_IMAGE_2D);
|
|
|
|
|
ENSURE(texture->GetFormat() == Format::R8G8B8A8_UNORM);
|
|
|
|
|
glBindImageTexture(textureUnit.unit, texture->As<CTexture>()->GetHandle(), 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8);
|
2024-09-12 07:31:43 -07:00
|
|
|
#endif
|
2024-01-17 11:40:27 -08:00
|
|
|
}
|
|
|
|
|
|
2025-05-21 03:29:47 -07:00
|
|
|
void CDeviceCommandContext::SetStorageBuffer([[maybe_unused]] const int32_t bindingSlot,
|
|
|
|
|
[[maybe_unused]] IBuffer* buffer)
|
2024-12-09 13:47:15 -08:00
|
|
|
{
|
|
|
|
|
#if !CONFIG2_GLES
|
|
|
|
|
if (bindingSlot < 0)
|
|
|
|
|
return;
|
|
|
|
|
ENSURE(m_ShaderProgram);
|
|
|
|
|
ENSURE(buffer);
|
|
|
|
|
ENSURE(buffer->GetUsage() & Renderer::Backend::IBuffer::Usage::STORAGE);
|
|
|
|
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, m_ShaderProgram->GetStorageBuffer(bindingSlot), buffer->As<CBuffer>()->GetHandle());
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-02 13:57:22 -07:00
|
|
|
void CDeviceCommandContext::SetUniform(
|
|
|
|
|
const int32_t bindingSlot,
|
|
|
|
|
const float value)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_ShaderProgram);
|
|
|
|
|
m_ShaderProgram->SetUniform(bindingSlot, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetUniform(
|
|
|
|
|
const int32_t bindingSlot,
|
|
|
|
|
const float valueX, const float valueY)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_ShaderProgram);
|
|
|
|
|
m_ShaderProgram->SetUniform(bindingSlot, valueX, valueY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetUniform(
|
|
|
|
|
const int32_t bindingSlot,
|
|
|
|
|
const float valueX, const float valueY,
|
|
|
|
|
const float valueZ)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_ShaderProgram);
|
|
|
|
|
m_ShaderProgram->SetUniform(bindingSlot, valueX, valueY, valueZ);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetUniform(
|
|
|
|
|
const int32_t bindingSlot,
|
|
|
|
|
const float valueX, const float valueY,
|
|
|
|
|
const float valueZ, const float valueW)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_ShaderProgram);
|
|
|
|
|
m_ShaderProgram->SetUniform(bindingSlot, valueX, valueY, valueZ, valueW);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetUniform(
|
2025-08-13 08:20:49 -07:00
|
|
|
const int32_t bindingSlot, std::span<const float> values)
|
2022-05-02 13:57:22 -07:00
|
|
|
{
|
|
|
|
|
ENSURE(m_ShaderProgram);
|
|
|
|
|
m_ShaderProgram->SetUniform(bindingSlot, values);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-13 11:30:28 -08:00
|
|
|
CDeviceCommandContext::ScopedBind::ScopedBind(
|
|
|
|
|
CDeviceCommandContext* deviceCommandContext,
|
|
|
|
|
const GLenum target, const GLuint handle)
|
|
|
|
|
: m_DeviceCommandContext(deviceCommandContext),
|
2022-05-02 13:57:22 -07:00
|
|
|
m_OldBindUnit(deviceCommandContext->m_BoundTextures[deviceCommandContext->m_ActiveTextureUnit]),
|
|
|
|
|
m_ActiveTextureUnit(deviceCommandContext->m_ActiveTextureUnit)
|
2022-02-13 11:30:28 -08:00
|
|
|
{
|
2022-05-02 13:57:22 -07:00
|
|
|
const uint32_t unit = m_DeviceCommandContext->m_BoundTextures.size() - 1;
|
|
|
|
|
m_DeviceCommandContext->BindTexture(unit, target, handle);
|
2022-02-13 11:30:28 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CDeviceCommandContext::ScopedBind::~ScopedBind()
|
|
|
|
|
{
|
|
|
|
|
m_DeviceCommandContext->BindTexture(
|
2022-05-02 13:57:22 -07:00
|
|
|
m_ActiveTextureUnit, m_OldBindUnit.target, m_OldBindUnit.handle);
|
2022-02-13 11:30:28 -08:00
|
|
|
}
|
|
|
|
|
|
2022-04-23 13:11:14 -07:00
|
|
|
CDeviceCommandContext::ScopedBufferBind::ScopedBufferBind(
|
|
|
|
|
CDeviceCommandContext* deviceCommandContext, CBuffer* buffer)
|
|
|
|
|
: m_DeviceCommandContext(deviceCommandContext)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(buffer);
|
|
|
|
|
m_CacheIndex = static_cast<size_t>(buffer->GetType());
|
2025-04-12 07:27:52 -07:00
|
|
|
ENSURE(m_CacheIndex < m_DeviceCommandContext->m_BoundBuffers.size());
|
2022-04-23 13:11:14 -07:00
|
|
|
const GLenum target = BufferTypeToGLTarget(buffer->GetType());
|
|
|
|
|
const GLuint handle = buffer->GetHandle();
|
2022-04-26 11:25:52 -07:00
|
|
|
if (m_DeviceCommandContext->m_BoundBuffers[m_CacheIndex].first == target &&
|
|
|
|
|
m_DeviceCommandContext->m_BoundBuffers[m_CacheIndex].second == handle)
|
|
|
|
|
{
|
|
|
|
|
// Use an invalid index as a sign that we don't need to restore the
|
|
|
|
|
// bound buffer.
|
|
|
|
|
m_CacheIndex = m_DeviceCommandContext->m_BoundBuffers.size();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
glBindBufferARB(target, handle);
|
|
|
|
|
}
|
2022-04-23 13:11:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CDeviceCommandContext::ScopedBufferBind::~ScopedBufferBind()
|
|
|
|
|
{
|
2022-04-26 11:25:52 -07:00
|
|
|
if (m_CacheIndex >= m_DeviceCommandContext->m_BoundBuffers.size())
|
|
|
|
|
return;
|
2022-04-23 13:11:14 -07:00
|
|
|
glBindBufferARB(
|
|
|
|
|
m_DeviceCommandContext->m_BoundBuffers[m_CacheIndex].first,
|
|
|
|
|
m_DeviceCommandContext->m_BoundBuffers[m_CacheIndex].second);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-05 06:49:54 -08:00
|
|
|
} // namespace GL
|
|
|
|
|
|
|
|
|
|
} // namespace Backend
|
|
|
|
|
|
|
|
|
|
} // namespace Renderer
|