// 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 "OpenGLBackend.h" | |
#include "CommandBufferGL.h" | |
#include "PipelineGL.h" | |
#include "PipelineLayoutGL.h" | |
#include "ShaderModuleGL.h" | |
#include "SamplerGL.h" | |
#include "TextureGL.h" | |
namespace backend { | |
namespace opengl { | |
nxtProcTable GetNonValidatingProcs(); | |
nxtProcTable GetValidatingProcs(); | |
void HACKCLEAR() { | |
glClearColor(0, 0, 0, 1); | |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
glClearStencil(0); | |
glClear(GL_STENCIL_BUFFER_BIT); | |
} | |
void Init(void* (*getProc)(const char*), nxtProcTable* procs, nxtDevice* device) { | |
*device = nullptr; | |
gladLoadGLLoader(reinterpret_cast<GLADloadproc>(getProc)); | |
glEnable(GL_DEPTH_TEST); | |
HACKCLEAR(); | |
*procs = GetValidatingProcs(); | |
*device = reinterpret_cast<nxtDevice>(new Device); | |
} | |
static GLuint OpenGLCompareFunction(nxt::CompareFunction compareFunction) { | |
switch (compareFunction) { | |
case nxt::CompareFunction::Never: | |
return GL_NEVER; | |
case nxt::CompareFunction::Less: | |
return GL_LESS; | |
case nxt::CompareFunction::LessEqual: | |
return GL_LEQUAL; | |
case nxt::CompareFunction::Greater: | |
return GL_GREATER; | |
case nxt::CompareFunction::GreaterEqual: | |
return GL_GEQUAL; | |
case nxt::CompareFunction::NotEqual: | |
return GL_NOTEQUAL; | |
case nxt::CompareFunction::Equal: | |
return GL_EQUAL; | |
case nxt::CompareFunction::Always: | |
return GL_ALWAYS; | |
default: | |
ASSERT(false); | |
} | |
} | |
static GLuint OpenGLStencilOperation(nxt::StencilOperation stencilOperation) { | |
switch (stencilOperation) { | |
case nxt::StencilOperation::Keep: | |
return GL_KEEP; | |
case nxt::StencilOperation::Zero: | |
return GL_ZERO; | |
case nxt::StencilOperation::Replace: | |
return GL_REPLACE; | |
case nxt::StencilOperation::Invert: | |
return GL_INVERT; | |
case nxt::StencilOperation::IncrementClamp: | |
return GL_INCR; | |
case nxt::StencilOperation::DecrementClamp: | |
return GL_DECR; | |
case nxt::StencilOperation::IncrementWrap: | |
return GL_INCR_WRAP; | |
case nxt::StencilOperation::DecrementWrap: | |
return GL_DECR_WRAP; | |
default: | |
ASSERT(false); | |
} | |
} | |
// Device | |
BindGroupBase* Device::CreateBindGroup(BindGroupBuilder* builder) { | |
return new BindGroup(this, builder); | |
} | |
BindGroupLayoutBase* Device::CreateBindGroupLayout(BindGroupLayoutBuilder* builder) { | |
return new BindGroupLayout(this, builder); | |
} | |
BufferBase* Device::CreateBuffer(BufferBuilder* builder) { | |
return new Buffer(this, builder); | |
} | |
BufferViewBase* Device::CreateBufferView(BufferViewBuilder* builder) { | |
return new BufferView(this, builder); | |
} | |
CommandBufferBase* Device::CreateCommandBuffer(CommandBufferBuilder* builder) { | |
return new CommandBuffer(this, builder); | |
} | |
DepthStencilStateBase* Device::CreateDepthStencilState(DepthStencilStateBuilder* builder) { | |
return new DepthStencilState(this, builder); | |
} | |
InputStateBase* Device::CreateInputState(InputStateBuilder* builder) { | |
return new InputState(this, builder); | |
} | |
FramebufferBase* Device::CreateFramebuffer(FramebufferBuilder* builder) { | |
return new Framebuffer(this, builder); | |
} | |
PipelineBase* Device::CreatePipeline(PipelineBuilder* builder) { | |
return new Pipeline(this, builder); | |
} | |
PipelineLayoutBase* Device::CreatePipelineLayout(PipelineLayoutBuilder* builder) { | |
return new PipelineLayout(this, builder); | |
} | |
QueueBase* Device::CreateQueue(QueueBuilder* builder) { | |
return new Queue(this, builder); | |
} | |
RenderPassBase* Device::CreateRenderPass(RenderPassBuilder* builder) { | |
return new RenderPass(this, builder); | |
} | |
SamplerBase* Device::CreateSampler(SamplerBuilder* builder) { | |
return new Sampler(this, builder); | |
} | |
ShaderModuleBase* Device::CreateShaderModule(ShaderModuleBuilder* builder) { | |
return new ShaderModule(this, builder); | |
} | |
TextureBase* Device::CreateTexture(TextureBuilder* builder) { | |
return new Texture(this, builder); | |
} | |
TextureViewBase* Device::CreateTextureView(TextureViewBuilder* builder) { | |
return new TextureView(this, builder); | |
} | |
void Device::Reference() { | |
} | |
void Device::Release() { | |
} | |
// Bind Group | |
BindGroup::BindGroup(Device* device, BindGroupBuilder* builder) | |
: BindGroupBase(builder), device(device) { | |
} | |
// Bind Group Layout | |
BindGroupLayout::BindGroupLayout(Device* device, BindGroupLayoutBuilder* builder) | |
: BindGroupLayoutBase(builder), device(device) { | |
} | |
// Buffer | |
Buffer::Buffer(Device* device, BufferBuilder* builder) | |
: BufferBase(builder), device(device) { | |
glGenBuffers(1, &buffer); | |
glBindBuffer(GL_ARRAY_BUFFER, buffer); | |
glBufferData(GL_ARRAY_BUFFER, GetSize(), nullptr, GL_STATIC_DRAW); | |
} | |
GLuint Buffer::GetHandle() const { | |
return buffer; | |
} | |
void Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) { | |
glBindBuffer(GL_ARRAY_BUFFER, buffer); | |
glBufferSubData(GL_ARRAY_BUFFER, start * sizeof(uint32_t), count * sizeof(uint32_t), data); | |
} | |
// BufferView | |
BufferView::BufferView(Device* device, BufferViewBuilder* builder) | |
: BufferViewBase(builder), device(device) { | |
} | |
// DepthStencilState | |
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 = GetStencil(nxt::Face::Back); | |
auto& front = GetStencil(nxt::Face::Front); | |
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 = GetStencil(nxt::Face::Back); | |
auto& front = GetStencil(nxt::Face::Front); | |
glStencilFuncSeparate(GL_BACK, | |
OpenGLCompareFunction(back.compareFunction), | |
backReference, | |
back.readMask | |
); | |
glStencilFuncSeparate(GL_FRONT, | |
OpenGLCompareFunction(front.compareFunction), | |
frontReference, | |
front.readMask | |
); | |
} | |
} | |
// InputState | |
InputState::InputState(Device* device, InputStateBuilder* builder) | |
: InputStateBase(builder), device(device) { | |
glGenVertexArrays(1, &vertexArrayObject); | |
glBindVertexArray(vertexArrayObject); | |
auto& attributesSetMask = GetAttributesSetMask(); | |
for (uint32_t location = 0; location < attributesSetMask.size(); ++location) { | |
if (!attributesSetMask[location]) { | |
continue; | |
} | |
auto attribute = GetAttribute(location); | |
glEnableVertexAttribArray(location); | |
auto input = GetInput(attribute.bindingSlot); | |
if (input.stride == 0) { | |
// Emulate a stride of zero (constant vertex attribute) by | |
// setting the attribute instance divisor to a huge number. | |
glVertexAttribDivisor(location, 0xffffffff); | |
} else { | |
switch (input.stepMode) { | |
case nxt::InputStepMode::Vertex: | |
break; | |
case nxt::InputStepMode::Instance: | |
glVertexAttribDivisor(location, 1); | |
break; | |
default: | |
ASSERT(false); | |
break; | |
} | |
} | |
} | |
} | |
GLuint InputState::GetVAO() { | |
return vertexArrayObject; | |
} | |
// Framebuffer | |
Framebuffer::Framebuffer(Device* device, FramebufferBuilder* builder) | |
: FramebufferBase(builder), device(device) { | |
} | |
// Queue | |
Queue::Queue(Device* device, QueueBuilder* builder) | |
: QueueBase(builder), device(device) { | |
} | |
void Queue::Submit(uint32_t numCommands, CommandBuffer* const * commands) { | |
for (uint32_t i = 0; i < numCommands; ++i) { | |
commands[i]->Execute(); | |
} | |
} | |
// RenderPass | |
RenderPass::RenderPass(Device* device, RenderPassBuilder* builder) | |
: RenderPassBase(builder), device(device) { | |
} | |
} | |
} |