Refactor DepthStencilState. TODO: add validation tests

- defaults to depth and stencil tests off
- whether or not depth and stencil tests are enabled is inferred from the comparison functions and stencil operations
- only one stencil reference. D3D12 does not support separate references
- change SetDepthWriteMode to SetDepthWriteEnabled and use a bool instead of enum
- Create PersistentPipelineState class for OpenGL backend with simple state tracking
- Add validation so DepthStencilState properties are only set once
- Update API usage in HelloDepthStencil
- refactor tracking of the DepthStencilState in the Metal backend
- validate that compute pipeline does not have a depth stencil state
This commit is contained in:
Austin Eng
2017-06-01 11:30:03 -04:00
committed by Corentin Wallez
parent f51be34864
commit 1063439d5d
20 changed files with 451 additions and 257 deletions

View File

@@ -16,6 +16,7 @@
#include "common/Commands.h"
#include "OpenGLBackend.h"
#include "PersistentPipelineStateGL.h"
#include "PipelineGL.h"
#include "PipelineLayoutGL.h"
#include "SamplerGL.h"
@@ -26,6 +27,8 @@
namespace backend {
namespace opengl {
PersistentPipelineState persistentPipelineState;
CommandBuffer::CommandBuffer(Device* device, CommandBufferBuilder* builder)
: CommandBufferBase(builder), device(device), commands(builder->AcquireCommands()) {
}
@@ -144,7 +147,7 @@ namespace opengl {
case Command::SetPipeline:
{
SetPipelineCmd* cmd = commands.NextCommand<SetPipelineCmd>();
ToBackend(cmd->pipeline)->ApplyNow();
ToBackend(cmd->pipeline)->ApplyNow(persistentPipelineState);
lastPipeline = ToBackend(cmd->pipeline).Get();
}
break;
@@ -182,7 +185,8 @@ namespace opengl {
{
SetStencilReferenceCmd* cmd = commands.NextCommand<SetStencilReferenceCmd>();
DepthStencilState* depthStencilState = ToBackend(lastPipeline->GetDepthStencilState());
depthStencilState->ApplyStencilReferenceNow(cmd->backReference, cmd->frontReference);
persistentPipelineState.UpdateStencilReference(cmd->reference);
persistentPipelineState.ApplyStencilNow();
}
break;

View File

@@ -14,6 +14,7 @@
#include "OpenGLBackend.h"
#include "CommandBufferGL.h"
#include "PersistentPipelineStateGL.h"
#include "PipelineGL.h"
#include "PipelineLayoutGL.h"
#include "SamplerGL.h"

View File

@@ -45,7 +45,7 @@ namespace opengl {
*device = reinterpret_cast<nxtDevice>(new Device);
}
static GLuint OpenGLCompareFunction(nxt::CompareFunction compareFunction) {
GLuint OpenGLCompareFunction(nxt::CompareFunction compareFunction) {
switch (compareFunction) {
case nxt::CompareFunction::Never:
return GL_NEVER;
@@ -68,7 +68,7 @@ namespace opengl {
}
}
static GLuint OpenGLStencilOperation(nxt::StencilOperation stencilOperation) {
GLuint OpenGLStencilOperation(nxt::StencilOperation stencilOperation) {
switch (stencilOperation) {
case nxt::StencilOperation::Keep:
return GL_KEEP;
@@ -188,69 +188,6 @@ namespace opengl {
DepthStencilState::DepthStencilState(Device* device, DepthStencilStateBuilder* builder)
: DepthStencilStateBase(builder), device(device) {
}
void DepthStencilState::ApplyNow() {
if (DepthIsEnabled()) {
glEnable(GL_DEPTH_TEST);
auto& depth = GetDepth();
glDepthFunc(OpenGLCompareFunction(depth.compareFunction));
switch (depth.depthWriteMode) {
case nxt::DepthWriteMode::Disabled:
glDepthMask(GL_FALSE);
break;
case nxt::DepthWriteMode::Enabled:
glDepthMask(GL_TRUE);
break;
default:
ASSERT(false);
break;
}
}
else {
glDisable(GL_DEPTH_TEST);
}
if (StencilIsEnabled()) {
glEnable(GL_STENCIL_TEST);
auto& back = GetBackStencil();
auto& front = GetFrontStencil();
glStencilOpSeparate(GL_BACK,
OpenGLStencilOperation(back.stencilFail),
OpenGLStencilOperation(back.depthFail),
OpenGLStencilOperation(back.stencilPass)
);
glStencilOpSeparate(GL_FRONT,
OpenGLStencilOperation(front.stencilFail),
OpenGLStencilOperation(front.depthFail),
OpenGLStencilOperation(front.stencilPass)
);
glStencilMaskSeparate(GL_BACK, back.writeMask);
glStencilMaskSeparate(GL_FRONT, front.writeMask);
}
else {
glDisable(GL_STENCIL_TEST);
}
}
void DepthStencilState::ApplyStencilReferenceNow(uint32_t backReference, uint32_t frontReference) {
if (StencilIsEnabled()) {
auto& back = GetBackStencil();
auto& front = GetFrontStencil();
glStencilFuncSeparate(GL_BACK,
OpenGLCompareFunction(back.compareFunction),
backReference,
back.readMask
);
glStencilFuncSeparate(GL_FRONT,
OpenGLCompareFunction(front.compareFunction),
frontReference,
front.readMask
);
}
}
// InputState

View File

@@ -40,6 +40,7 @@ namespace opengl {
class CommandBuffer;
class DepthStencilState;
class InputState;
class PersistentPipelineState;
class Pipeline;
class PipelineLayout;
class Queue;
@@ -74,6 +75,9 @@ namespace opengl {
return ToBackendBase<OpenGLBackendTraits>(common);
}
GLuint OpenGLCompareFunction(nxt::CompareFunction compareFunction);
GLuint OpenGLStencilOperation(nxt::StencilOperation stencilOperation);
// Definition of backend types
class Device : public DeviceBase {
public:
@@ -139,8 +143,6 @@ namespace opengl {
class DepthStencilState : public DepthStencilStateBase {
public:
DepthStencilState(Device* device, DepthStencilStateBuilder* builder);
void ApplyNow();
void ApplyStencilReferenceNow(uint32_t backReference, uint32_t frontReference);
private:
Device* device;

View File

@@ -0,0 +1,169 @@
// Copyright 2017 The NXT Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "PersistentPipelineStateGL.h"
#include "OpenGLBackend.h"
namespace backend {
namespace opengl {
PersistentPipelineState::PersistentPipelineState() {
dirtyFields.set(); // initialize all fields as dirty
}
// when a field on PersistentPipelineState::State changes, mark its starting location as dirty
#define SET_FIELD(field, destination, value) { \
if (state.destination != value) { \
dirtyFields.set(field); \
state.destination = value; \
} \
}
void PersistentPipelineState::UpdateDepthStencilInfo(const DepthStencilStateBase* const depthStencilState) {
auto& depth = depthStencilState->GetDepth();
SET_FIELD(DEPTH_COMPARE_FUNCTION, depthInfo.compareFunction, depth.compareFunction)
SET_FIELD(DEPTH_WRITE_ENABLED, depthInfo.depthWriteEnabled, depth.depthWriteEnabled)
SET_FIELD(DEPTH_ENABLED, depthEnabled, depthStencilState->DepthTestEnabled())
auto& stencil = depthStencilState->GetStencil();
SET_FIELD(STENCIL_ENABLED, stencilEnabled, depthStencilState->StencilTestEnabled())
SET_FIELD(STENCIL_BACK_COMPARE_FUNCTION, stencilInfo.back.compareFunction, stencil.back.compareFunction)
SET_FIELD(STENCIL_BACK_STENCIL_FAIL, stencilInfo.back.stencilFail, stencil.back.stencilFail)
SET_FIELD(STENCIL_BACK_DEPTH_FAIL, stencilInfo.back.depthFail, stencil.back.depthFail)
SET_FIELD(STENCIL_BACK_DEPTH_STENCIL_PASS, stencilInfo.back.depthStencilPass, stencil.back.depthStencilPass)
SET_FIELD(STENCIL_BACK_MASK, stencilInfo.back.mask, stencil.back.mask)
SET_FIELD(STENCIL_FRONT_COMPARE_FUNCTION, stencilInfo.front.compareFunction, stencil.front.compareFunction)
SET_FIELD(STENCIL_FRONT_STENCIL_FAIL, stencilInfo.front.stencilFail, stencil.front.stencilFail)
SET_FIELD(STENCIL_FRONT_DEPTH_FAIL, stencilInfo.front.depthFail, stencil.front.depthFail)
SET_FIELD(STENCIL_FRONT_DEPTH_STENCIL_PASS, stencilInfo.front.depthStencilPass, stencil.front.depthStencilPass)
SET_FIELD(STENCIL_FRONT_MASK, stencilInfo.front.mask, stencil.front.mask)
}
void PersistentPipelineState::UpdateStencilReference(uint32_t stencilReference) {
SET_FIELD(STENCIL_REFERENCE, stencilReference, stencilReference)
}
#undef SET_FIELD
bool PersistentPipelineState::IsDirty(Field field) const {
return dirtyFields.test(field);
}
void PersistentPipelineState::CleanField(Field field) {
dirtyFields.reset(field);
}
void PersistentPipelineState::ApplyDepthNow() {
if (IsDirty(DEPTH_ENABLED)) {
if (state.depthEnabled) {
glEnable(GL_DEPTH_TEST);
} else {
glDisable(GL_DEPTH_TEST);
}
CleanField(DEPTH_ENABLED);
}
if (IsDirty(DEPTH_WRITE_ENABLED)) {
if (state.depthInfo.depthWriteEnabled) {
glDepthMask(GL_TRUE);
} else {
glDepthMask(GL_FALSE);
}
CleanField(DEPTH_WRITE_ENABLED);
}
if (IsDirty(DEPTH_COMPARE_FUNCTION)) {
glDepthFunc(OpenGLCompareFunction(state.depthInfo.compareFunction));
CleanField(DEPTH_COMPARE_FUNCTION);
}
}
void PersistentPipelineState::ApplyStencilNow() {
if (IsDirty(STENCIL_ENABLED)) {
if (state.stencilEnabled) {
glEnable(GL_STENCIL_TEST);
} else {
glDisable(GL_STENCIL_TEST);
}
CleanField(STENCIL_ENABLED);
}
if (IsDirty(STENCIL_BACK_STENCIL_FAIL) ||
IsDirty(STENCIL_BACK_DEPTH_FAIL) ||
IsDirty(STENCIL_BACK_DEPTH_STENCIL_PASS)) {
glStencilOpSeparate(GL_BACK,
OpenGLStencilOperation(state.stencilInfo.back.stencilFail),
OpenGLStencilOperation(state.stencilInfo.back.depthFail),
OpenGLStencilOperation(state.stencilInfo.back.depthStencilPass)
);
CleanField(STENCIL_BACK_STENCIL_FAIL);
CleanField(STENCIL_BACK_DEPTH_FAIL);
CleanField(STENCIL_BACK_DEPTH_STENCIL_PASS);
}
if (IsDirty(STENCIL_BACK_COMPARE_FUNCTION) ||
IsDirty(STENCIL_REFERENCE) ||
IsDirty(STENCIL_BACK_MASK)) {
glStencilFuncSeparate(GL_BACK,
OpenGLCompareFunction(state.stencilInfo.back.compareFunction),
state.stencilReference,
state.stencilInfo.back.mask
);
if (IsDirty(STENCIL_BACK_MASK)) {
glStencilMaskSeparate(GL_BACK, state.stencilInfo.back.mask);
}
CleanField(STENCIL_BACK_COMPARE_FUNCTION);
CleanField(STENCIL_BACK_MASK);
}
if (IsDirty(STENCIL_FRONT_STENCIL_FAIL) ||
IsDirty(STENCIL_FRONT_DEPTH_FAIL) ||
IsDirty(STENCIL_FRONT_DEPTH_STENCIL_PASS)) {
glStencilOpSeparate(GL_FRONT,
OpenGLStencilOperation(state.stencilInfo.front.stencilFail),
OpenGLStencilOperation(state.stencilInfo.front.depthFail),
OpenGLStencilOperation(state.stencilInfo.front.depthStencilPass)
);
CleanField(STENCIL_FRONT_STENCIL_FAIL);
CleanField(STENCIL_FRONT_DEPTH_FAIL);
CleanField(STENCIL_FRONT_DEPTH_STENCIL_PASS);
}
if (IsDirty(STENCIL_FRONT_COMPARE_FUNCTION) ||
IsDirty(STENCIL_REFERENCE) ||
IsDirty(STENCIL_FRONT_MASK)) {
glStencilFuncSeparate(GL_FRONT,
OpenGLCompareFunction(state.stencilInfo.front.compareFunction),
state.stencilReference,
state.stencilInfo.front.mask
);
if (IsDirty(STENCIL_FRONT_MASK)) {
glStencilMaskSeparate(GL_FRONT, state.stencilInfo.front.mask);
}
CleanField(STENCIL_FRONT_COMPARE_FUNCTION);
CleanField(STENCIL_FRONT_MASK);
}
CleanField(STENCIL_REFERENCE); // clean this last because its used for both the back and front functions
}
}
}

View File

@@ -0,0 +1,72 @@
// Copyright 2017 The NXT Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef BACKEND_OPENGL_PERSISTENTPIPELINESTATE_H_
#define BACKEND_OPENGL_PERSISTENTPIPELINESTATE_H_
#include "common/DepthStencilState.h"
#include <bitset>
namespace backend {
namespace opengl {
class PersistentPipelineState {
public:
PersistentPipelineState();
void UpdateDepthStencilInfo(const DepthStencilStateBase* const depthStencilState);
void UpdateStencilReference(uint32_t stencilReference);
void ApplyDepthNow();
void ApplyStencilNow();
enum Field {
DEPTH_COMPARE_FUNCTION,
DEPTH_WRITE_ENABLED,
DEPTH_ENABLED,
STENCIL_ENABLED,
STENCIL_BACK_COMPARE_FUNCTION,
STENCIL_BACK_STENCIL_FAIL,
STENCIL_BACK_DEPTH_FAIL,
STENCIL_BACK_DEPTH_STENCIL_PASS,
STENCIL_BACK_MASK,
STENCIL_FRONT_COMPARE_FUNCTION,
STENCIL_FRONT_STENCIL_FAIL,
STENCIL_FRONT_DEPTH_FAIL,
STENCIL_FRONT_DEPTH_STENCIL_PASS,
STENCIL_FRONT_MASK,
STENCIL_REFERENCE,
Count
};
struct State {
bool depthEnabled;
bool stencilEnabled;
DepthStencilStateBase::DepthInfo depthInfo;
DepthStencilStateBase::StencilInfo stencilInfo;
uint32_t stencilReference;
};
private:
State state;
std::bitset<Field::Count> dirtyFields;
inline bool IsDirty(Field field) const;
inline void CleanField(Field field);
};
}
}
#endif // BACKEND_OPENGL_PERSISTENTPIPELINESTATE_H_

View File

@@ -15,6 +15,7 @@
#include "PipelineGL.h"
#include "OpenGLBackend.h"
#include "PersistentPipelineStateGL.h"
#include "PipelineLayoutGL.h"
#include "ShaderModuleGL.h"
@@ -202,13 +203,15 @@ namespace opengl {
return program;
}
void Pipeline::ApplyNow() {
void Pipeline::ApplyNow(PersistentPipelineState &persistentPipelineState) {
glUseProgram(program);
auto inputState = ToBackend(GetInputState());
glBindVertexArray(inputState->GetVAO());
auto depthStencilState = ToBackend(GetDepthStencilState());
depthStencilState->ApplyNow();
persistentPipelineState.UpdateDepthStencilInfo(GetDepthStencilState());
persistentPipelineState.ApplyDepthNow();
persistentPipelineState.ApplyStencilNow();
}
}

View File

@@ -25,6 +25,7 @@ namespace backend {
namespace opengl {
class Device;
class PersistentPipelineState;
class ShaderModule;
class Pipeline : public PipelineBase {
@@ -39,7 +40,7 @@ namespace opengl {
const std::vector<GLuint>& GetTextureUnitsForTexture(GLuint index) const;
GLuint GetProgramHandle() const;
void ApplyNow();
void ApplyNow(PersistentPipelineState &persistentPipelineState);
private:
GLuint program;