OpenGL: Fix push constants disappearing on pipeline change

This commit is contained in:
Corentin Wallez 2017-08-14 16:25:26 -04:00 committed by Corentin Wallez
parent 3ef4121d4e
commit a214b7f12d
2 changed files with 77 additions and 29 deletions

View File

@ -29,6 +29,69 @@
namespace backend {
namespace opengl {
namespace {
// Push constants are implemented using OpenGL uniforms, however they aren't part of the global
// OpenGL state but are part of the program state instead. This means that we have to reapply
// push constants on pipeline change.
//
// This structure tracks the current values of push constants as well as dirty bits for push constants
// that should be applied before the next draw or dispatch.
struct PushConstantTracker {
PerStage<std::array<uint32_t, kMaxPushConstants>> values;
PerStage<std::bitset<kMaxPushConstants>> dirtyBits;
void OnBeginPass() {
for (auto stage : IterateStages(kAllStages)) {
values[stage].fill(0);
// No need to set dirty bits are a pipeline will be set before the next operation
// using push constants.
}
}
void OnSetPushConstants(nxt::ShaderStageBit stages, uint32_t count,
uint32_t offset, const uint32_t* data) {
for (auto stage : IterateStages(stages)) {
memcpy(&values[stage][offset], data, count * sizeof(uint32_t));
// Use 64 bit masks and make sure there are no shift UB
static_assert(kMaxPushConstants <= 8 * sizeof(unsigned long long) - 1, "");
dirtyBits[stage] |= ((1ull << count) - 1ull) << offset;
}
}
void OnSetPipeline(PipelineBase* pipeline) {
for (auto stage : IterateStages(kAllStages)) {
dirtyBits[stage] = pipeline->GetPushConstants(stage).mask;
}
}
void Apply(PipelineBase* pipeline, PipelineGL* glPipeline) {
for (auto stage : IterateStages(kAllStages)) {
const auto& pushConstants = pipeline->GetPushConstants(stage);
const auto& glPushConstants = glPipeline->GetGLPushConstants(stage);
for (uint32_t constant : IterateBitSet(dirtyBits[stage] & pushConstants.mask)) {
GLint location = glPushConstants[constant];
switch (pushConstants.types[constant]) {
case PushConstantType::Int:
glUniform1i(location, *reinterpret_cast<GLint*>(&values[stage][constant]));
break;
case PushConstantType::UInt:
glUniform1ui(location, *reinterpret_cast<GLuint*>(&values[stage][constant]));
break;
case PushConstantType::Float:
glUniform1f(location, *reinterpret_cast<GLfloat*>(&values[stage][constant]));
break;
}
}
}
}
};
}
CommandBuffer::CommandBuffer(CommandBufferBuilder* builder)
: CommandBufferBase(builder), commands(builder->AcquireCommands()) {
}
@ -71,6 +134,8 @@ namespace opengl {
PersistentPipelineState persistentPipelineState;
persistentPipelineState.SetDefaultState();
PushConstantTracker pushConstants;
RenderPass* currentRenderPass = nullptr;
Framebuffer* currentFramebuffer = nullptr;
uint32_t currentSubpass = 0;
@ -81,6 +146,7 @@ namespace opengl {
case Command::BeginComputePass:
{
commands.NextCommand<BeginComputePassCmd>();
pushConstants.OnBeginPass();
}
break;
@ -96,6 +162,7 @@ namespace opengl {
case Command::BeginRenderSubpass:
{
commands.NextCommand<BeginRenderSubpassCmd>();
pushConstants.OnBeginPass();
// TODO(kainino@chromium.org): This is added to possibly
// work around an issue seen on Windows/Intel. It should
@ -284,6 +351,7 @@ namespace opengl {
case Command::Dispatch:
{
DispatchCmd* dispatch = commands.NextCommand<DispatchCmd>();
pushConstants.Apply(lastPipeline, lastGLPipeline);
glDispatchCompute(dispatch->x, dispatch->y, dispatch->z);
// TODO(cwallez@chromium.org): add barriers to the API
glMemoryBarrier(GL_ALL_BARRIER_BITS);
@ -293,6 +361,8 @@ namespace opengl {
case Command::DrawArrays:
{
DrawArraysCmd* draw = commands.NextCommand<DrawArraysCmd>();
pushConstants.Apply(lastPipeline, lastGLPipeline);
if (draw->firstInstance > 0) {
glDrawArraysInstancedBaseInstance(lastRenderPipeline->GetGLPrimitiveTopology(),
draw->firstVertex, draw->vertexCount, draw->instanceCount, draw->firstInstance);
@ -307,6 +377,8 @@ namespace opengl {
case Command::DrawElements:
{
DrawElementsCmd* draw = commands.NextCommand<DrawElementsCmd>();
pushConstants.Apply(lastPipeline, lastGLPipeline);
size_t formatSize = IndexFormatSize(indexBufferFormat);
GLenum formatType = IndexFormatType(indexBufferFormat);
@ -352,6 +424,7 @@ namespace opengl {
ToBackend(cmd->pipeline)->ApplyNow();
lastGLPipeline = ToBackend(cmd->pipeline).Get();
lastPipeline = ToBackend(cmd->pipeline).Get();
pushConstants.OnSetPipeline(lastPipeline);
}
break;
@ -362,35 +435,15 @@ namespace opengl {
lastRenderPipeline = ToBackend(cmd->pipeline).Get();
lastGLPipeline = ToBackend(cmd->pipeline).Get();
lastPipeline = ToBackend(cmd->pipeline).Get();
pushConstants.OnSetPipeline(lastPipeline);
}
break;
case Command::SetPushConstants:
{
SetPushConstantsCmd* cmd = commands.NextCommand<SetPushConstantsCmd>();
uint32_t* valuesUInt = commands.NextData<uint32_t>(cmd->count);
int32_t* valuesInt = reinterpret_cast<int32_t*>(valuesUInt);
float* valuesFloat = reinterpret_cast<float*>(valuesUInt);
for (auto stage : IterateStages(cmd->stages)) {
const auto& pushConstants = lastPipeline->GetPushConstants(stage);
const auto& glPushConstants = lastGLPipeline->GetGLPushConstants(stage);
for (size_t i = 0; i < cmd->count; i++) {
GLint location = glPushConstants[cmd->offset + i];
switch (pushConstants.types[cmd->offset + i]) {
case PushConstantType::Int:
glUniform1i(location, valuesInt[i]);
break;
case PushConstantType::UInt:
glUniform1ui(location, valuesUInt[i]);
break;
case PushConstantType::Float:
glUniform1f(location, valuesFloat[i]);
break;
}
}
}
uint32_t* data = commands.NextData<uint32_t>(cmd->count);
pushConstants.OnSetPushConstants(cmd->stages, cmd->count, cmd->offset, data);
}
break;
@ -418,11 +471,7 @@ namespace opengl {
const auto& layout = group->GetLayout()->GetBindingInfo();
// TODO(cwallez@chromium.org): iterate over the layout bitmask instead
for (size_t binding = 0; binding < kMaxBindingsPerGroup; ++binding) {
if (!layout.mask[binding]) {
continue;
}
for (uint32_t binding : IterateBitSet(layout.mask)) {
switch (layout.types[binding]) {
case nxt::BindingType::UniformBuffer:
{

View File

@ -59,7 +59,6 @@ namespace opengl {
auto depthStencilState = ToBackend(GetDepthStencilState());
depthStencilState->ApplyNow(persistentPipelineState);
RenderPass* renderPass = ToBackend(GetRenderPass());
auto& subpassInfo = renderPass->GetSubpassInfo(GetSubPass());