Initial commit of all the NXT integration.
More like squashed history, contributors were:
- Kai Ninomiya
- Corentin Wallez
diff --git a/src/backend/opengl/CommandBufferGL.cpp b/src/backend/opengl/CommandBufferGL.cpp
new file mode 100644
index 0000000..a62da6e
--- /dev/null
+++ b/src/backend/opengl/CommandBufferGL.cpp
@@ -0,0 +1,303 @@
+// 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 "CommandBufferGL.h"
+
+#include "common/Commands.h"
+#include "OpenGLBackend.h"
+#include "PipelineGL.h"
+#include "PipelineLayoutGL.h"
+#include "SamplerGL.h"
+#include "TextureGL.h"
+
+#include <cstring>
+
+namespace backend {
+namespace opengl {
+
+ CommandBuffer::CommandBuffer(Device* device, CommandBufferBuilder* builder)
+ : CommandBufferBase(builder), device(device), commands(builder->AcquireCommands()) {
+ }
+
+ CommandBuffer::~CommandBuffer() {
+ FreeCommands(&commands);
+ }
+
+ static GLenum IndexFormatType(nxt::IndexFormat format) {
+ switch (format) {
+ case nxt::IndexFormat::Uint16:
+ return GL_UNSIGNED_SHORT;
+ case nxt::IndexFormat::Uint32:
+ return GL_UNSIGNED_INT;
+ }
+ }
+
+ static GLenum VertexFormatType(nxt::VertexFormat format) {
+ switch (format) {
+ case nxt::VertexFormat::FloatR32G32B32A32:
+ case nxt::VertexFormat::FloatR32G32B32:
+ case nxt::VertexFormat::FloatR32G32:
+ return GL_FLOAT;
+ }
+ }
+
+ void CommandBuffer::Execute() {
+ Command type;
+ Pipeline* lastPipeline = nullptr;
+ uint32_t indexBufferOffset = 0;
+ nxt::IndexFormat indexBufferFormat = nxt::IndexFormat::Uint16;
+
+ while(commands.NextCommandId(&type)) {
+ switch (type) {
+
+ case Command::CopyBufferToTexture:
+ {
+ CopyBufferToTextureCmd* copy = commands.NextCommand<CopyBufferToTextureCmd>();
+ Buffer* buffer = ToBackend(copy->buffer.Get());
+ Texture* texture = ToBackend(copy->texture.Get());
+ GLenum target = texture->GetGLTarget();
+ auto format = texture->GetGLFormat();
+
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->GetHandle());
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(target, texture->GetHandle());
+
+ glTexSubImage2D(target, copy->level, copy->x, copy->y, copy->width, copy->height,
+ format.format, format.type, nullptr);
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+ }
+ break;
+
+ case Command::Dispatch:
+ {
+ DispatchCmd* dispatch = commands.NextCommand<DispatchCmd>();
+ glDispatchCompute(dispatch->x, dispatch->y, dispatch->z);
+ // TODO(cwallez@chromium.org): add barriers to the API
+ glMemoryBarrier(GL_ALL_BARRIER_BITS);
+ }
+ break;
+
+ case Command::DrawArrays:
+ {
+ DrawArraysCmd* draw = commands.NextCommand<DrawArraysCmd>();
+ if (draw->firstInstance > 0) {
+ glDrawArraysInstancedBaseInstance(GL_TRIANGLES,
+ draw->firstVertex, draw->vertexCount, draw->instanceCount, draw->firstInstance);
+ } else {
+ // This branch is only needed on OpenGL < 4.2
+ glDrawArraysInstanced(GL_TRIANGLES,
+ draw->firstVertex, draw->vertexCount, draw->instanceCount);
+ }
+ }
+ break;
+
+ case Command::DrawElements:
+ {
+ DrawElementsCmd* draw = commands.NextCommand<DrawElementsCmd>();
+ size_t formatSize = IndexFormatSize(indexBufferFormat);
+ GLenum formatType = IndexFormatType(indexBufferFormat);
+
+ if (draw->firstInstance > 0) {
+ glDrawElementsInstancedBaseInstance(GL_TRIANGLES,
+ draw->indexCount, formatType,
+ reinterpret_cast<void*>(draw->firstIndex * formatSize + indexBufferOffset),
+ draw->instanceCount, draw->firstInstance);
+ } else {
+ // This branch is only needed on OpenGL < 4.2
+ glDrawElementsInstanced(GL_TRIANGLES,
+ draw->indexCount, formatType,
+ reinterpret_cast<void*>(draw->firstIndex * formatSize + indexBufferOffset),
+ draw->instanceCount);
+ }
+ }
+ break;
+
+ case Command::SetPipeline:
+ {
+ SetPipelineCmd* cmd = commands.NextCommand<SetPipelineCmd>();
+ ToBackend(cmd->pipeline)->ApplyNow();
+ lastPipeline = ToBackend(cmd->pipeline).Get();
+ }
+ 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->stage)) {
+ const auto& pushConstants = lastPipeline->GetPushConstants(stage);
+ const auto& glPushConstants = lastPipeline->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;
+ }
+ }
+ }
+ }
+ break;
+
+ case Command::SetBindGroup:
+ {
+ SetBindGroupCmd* cmd = commands.NextCommand<SetBindGroupCmd>();
+ size_t index = cmd->index;
+ BindGroup* group = ToBackend(cmd->group.Get());
+
+ const auto& indices = ToBackend(lastPipeline->GetLayout())->GetBindingIndexInfo()[index];
+ 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;
+ }
+
+ switch (layout.types[binding]) {
+ case nxt::BindingType::UniformBuffer:
+ {
+ BufferView* view = ToBackend(group->GetBindingAsBufferView(binding));
+ GLuint buffer = ToBackend(view->GetBuffer())->GetHandle();
+ GLuint index = indices[binding];
+
+ glBindBufferRange(GL_UNIFORM_BUFFER, index, buffer, view->GetOffset(), view->GetSize());
+ }
+ break;
+
+ case nxt::BindingType::Sampler:
+ {
+ GLuint sampler = ToBackend(group->GetBindingAsSampler(binding))->GetHandle();
+ GLuint index = indices[binding];
+
+ for (auto unit : lastPipeline->GetTextureUnitsForSampler(index)) {
+ glBindSampler(unit, sampler);
+ }
+ }
+ break;
+
+ case nxt::BindingType::SampledTexture:
+ {
+ TextureView* view = ToBackend(group->GetBindingAsTextureView(binding));
+ Texture* texture = ToBackend(view->GetTexture());
+ GLuint handle = texture->GetHandle();
+ GLenum target = texture->GetGLTarget();
+ GLuint index = indices[binding];
+
+ for (auto unit : lastPipeline->GetTextureUnitsForTexture(index)) {
+ glActiveTexture(GL_TEXTURE0 + unit);
+ glBindTexture(target, handle);
+ }
+ }
+ break;
+
+ case nxt::BindingType::StorageBuffer:
+ {
+ BufferView* view = ToBackend(group->GetBindingAsBufferView(binding));
+ GLuint buffer = ToBackend(view->GetBuffer())->GetHandle();
+ GLuint index = indices[binding];
+
+ glBindBufferRange(GL_SHADER_STORAGE_BUFFER, index, buffer, view->GetOffset(), view->GetSize());
+ }
+ break;
+ }
+ }
+ }
+ break;
+
+ case Command::SetIndexBuffer:
+ {
+ SetIndexBufferCmd* cmd = commands.NextCommand<SetIndexBufferCmd>();
+
+ GLuint buffer = ToBackend(cmd->buffer.Get())->GetHandle();
+ indexBufferOffset = cmd->offset;
+ indexBufferFormat = cmd->format;
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
+ }
+ break;
+
+ case Command::SetVertexBuffers:
+ {
+ SetVertexBuffersCmd* cmd = commands.NextCommand<SetVertexBuffersCmd>();
+ auto buffers = commands.NextData<Ref<BufferBase>>(cmd->count);
+ auto offsets = commands.NextData<uint32_t>(cmd->count);
+
+ auto inputState = lastPipeline->GetInputState();
+
+ auto& attributesSetMask = inputState->GetAttributesSetMask();
+ for (uint32_t location = 0; location < attributesSetMask.size(); ++location) {
+ if (!attributesSetMask[location]) {
+ // This slot is not used in the input state
+ continue;
+ }
+ auto attribute = inputState->GetAttribute(location);
+ auto slot = attribute.bindingSlot;
+ ASSERT(slot < kMaxVertexInputs);
+ if (slot < cmd->startSlot || slot >= cmd->startSlot + cmd->count) {
+ // This slot is not affected by this call
+ continue;
+ }
+ size_t bufferIndex = slot - cmd->startSlot;
+ GLuint buffer = ToBackend(buffers[bufferIndex])->GetHandle();
+ uint32_t bufferOffset = offsets[bufferIndex];
+
+ auto input = inputState->GetInput(slot);
+
+ auto components = VertexFormatNumComponents(attribute.format);
+ auto formatType = VertexFormatType(attribute.format);
+
+ glBindBuffer(GL_ARRAY_BUFFER, buffer);
+ glVertexAttribPointer(
+ location, components, formatType, GL_FALSE,
+ input.stride,
+ reinterpret_cast<void*>(static_cast<intptr_t>(bufferOffset + attribute.offset)));
+ }
+ }
+ break;
+
+ case Command::TransitionBufferUsage:
+ {
+ TransitionBufferUsageCmd* cmd = commands.NextCommand<TransitionBufferUsageCmd>();
+
+ cmd->buffer->TransitionUsageImpl(cmd->usage);
+ }
+ break;
+
+ case Command::TransitionTextureUsage:
+ {
+ TransitionTextureUsageCmd* cmd = commands.NextCommand<TransitionTextureUsageCmd>();
+
+ cmd->texture->TransitionUsageImpl(cmd->usage);
+ }
+ break;
+ }
+ }
+
+ // HACK: cleanup a tiny bit of state to make this work with
+ // virtualized contexts enabled in Chromium
+ glBindSampler(0, 0);
+ }
+
+}
+}
diff --git a/src/backend/opengl/CommandBufferGL.h b/src/backend/opengl/CommandBufferGL.h
new file mode 100644
index 0000000..1492551
--- /dev/null
+++ b/src/backend/opengl/CommandBufferGL.h
@@ -0,0 +1,45 @@
+// 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_COMMANDBUFFER_H_
+#define BACKEND_OPENGL_COMMANDBUFFER_H_
+
+#include "common/CommandAllocator.h"
+#include "common/CommandBuffer.h"
+
+namespace backend {
+ class CommandBufferBuilder;
+}
+
+namespace backend {
+namespace opengl {
+
+ class Device;
+
+ class CommandBuffer : public CommandBufferBase {
+ public:
+ CommandBuffer(Device* device, CommandBufferBuilder* builder);
+ ~CommandBuffer();
+
+ void Execute();
+
+ private:
+ Device* device;
+ CommandIterator commands;
+ };
+
+}
+}
+
+#endif // BACKEND_OPENGL_COMMANDBUFFER_H_
diff --git a/src/backend/opengl/GeneratedCodeIncludes.h b/src/backend/opengl/GeneratedCodeIncludes.h
new file mode 100644
index 0000000..f757618
--- /dev/null
+++ b/src/backend/opengl/GeneratedCodeIncludes.h
@@ -0,0 +1,21 @@
+// 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 "SamplerGL.h"
+#include "ShaderModuleGL.h"
+#include "TextureGL.h"
diff --git a/src/backend/opengl/OpenGLBackend.cpp b/src/backend/opengl/OpenGLBackend.cpp
new file mode 100644
index 0000000..7455626
--- /dev/null
+++ b/src/backend/opengl/OpenGLBackend.cpp
@@ -0,0 +1,180 @@
+// 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);
+ }
+
+ 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);
+ }
+
+ // 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);
+ }
+ InputStateBase* Device::CreateInputState(InputStateBuilder* builder) {
+ return new InputState(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);
+ }
+ 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) {
+ }
+
+ // 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;
+ }
+
+ // Queue
+
+ Queue::Queue(Device* device, QueueBuilder* builder) : device(device) {
+ }
+
+ void Queue::Submit(uint32_t numCommands, CommandBuffer* const * commands) {
+ for (uint32_t i = 0; i < numCommands; ++i) {
+ commands[i]->Execute();
+ }
+ }
+
+}
+}
diff --git a/src/backend/opengl/OpenGLBackend.h b/src/backend/opengl/OpenGLBackend.h
new file mode 100644
index 0000000..41742ea
--- /dev/null
+++ b/src/backend/opengl/OpenGLBackend.h
@@ -0,0 +1,151 @@
+// 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_OPENGLBACKEND_H_
+#define BACKEND_OPENGL_OPENGLBACKEND_H_
+
+#include "nxt/nxtcpp.h"
+
+#include "common/Buffer.h"
+#include "common/BindGroup.h"
+#include "common/BindGroupLayout.h"
+#include "common/Device.h"
+#include "common/InputState.h"
+#include "common/Queue.h"
+#include "common/ToBackend.h"
+
+#include "glad/glad.h"
+
+namespace backend {
+namespace opengl {
+
+ class BindGroup;
+ class BindGroupLayout;
+ class Buffer;
+ class BufferView;
+ class CommandBuffer;
+ class InputState;
+ class Pipeline;
+ class PipelineLayout;
+ class Queue;
+ class Sampler;
+ class ShaderModule;
+ class Texture;
+ class TextureView;
+
+ struct OpenGLBackendTraits {
+ using BindGroupType = BindGroup;
+ using BindGroupLayoutType = BindGroupLayout;
+ using BufferType = Buffer;
+ using BufferViewType = BufferView;
+ using CommandBufferType = CommandBuffer;
+ using InputStateType = InputState;
+ using PipelineType = Pipeline;
+ using PipelineLayoutType = PipelineLayout;
+ using QueueType = Queue;
+ using SamplerType = Sampler;
+ using ShaderModuleType = ShaderModule;
+ using TextureType = Texture;
+ using TextureViewType = TextureView;
+ };
+
+ template<typename T>
+ auto ToBackend(T&& common) -> decltype(ToBackendBase<OpenGLBackendTraits>(common)) {
+ return ToBackendBase<OpenGLBackendTraits>(common);
+ }
+
+ // Definition of backend types
+ class Device : public DeviceBase {
+ public:
+ BindGroupBase* CreateBindGroup(BindGroupBuilder* builder) override;
+ BindGroupLayoutBase* CreateBindGroupLayout(BindGroupLayoutBuilder* builder) override;
+ BufferBase* CreateBuffer(BufferBuilder* builder) override;
+ BufferViewBase* CreateBufferView(BufferViewBuilder* builder) override;
+ CommandBufferBase* CreateCommandBuffer(CommandBufferBuilder* builder) override;
+ InputStateBase* CreateInputState(InputStateBuilder* builder) override;
+ PipelineBase* CreatePipeline(PipelineBuilder* builder) override;
+ PipelineLayoutBase* CreatePipelineLayout(PipelineLayoutBuilder* builder) override;
+ QueueBase* CreateQueue(QueueBuilder* builder) override;
+ SamplerBase* CreateSampler(SamplerBuilder* builder) override;
+ ShaderModuleBase* CreateShaderModule(ShaderModuleBuilder* builder) override;
+ TextureBase* CreateTexture(TextureBuilder* builder) override;
+ TextureViewBase* CreateTextureView(TextureViewBuilder* builder) override;
+
+ // NXT API
+ void Reference();
+ void Release();
+ };
+
+ class BindGroup : public BindGroupBase {
+ public:
+ BindGroup(Device* device, BindGroupBuilder* builder);
+
+ private:
+ Device* device;
+ };
+
+ class BindGroupLayout : public BindGroupLayoutBase {
+ public:
+ BindGroupLayout(Device* device, BindGroupLayoutBuilder* builder);
+
+ private:
+ Device* device;
+ };
+
+ class Buffer : public BufferBase {
+ public:
+ Buffer(Device* device, BufferBuilder* builder);
+
+ GLuint GetHandle() const;
+
+ private:
+ void SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) override;
+
+ Device* device;
+ GLuint buffer = 0;
+ };
+
+ class BufferView : public BufferViewBase {
+ public:
+ BufferView(Device* device, BufferViewBuilder* builder);
+
+ private:
+ Device* device;
+ };
+
+ class InputState : public InputStateBase {
+ public:
+ InputState(Device* device, InputStateBuilder* builder);
+ GLuint GetVAO();
+
+ private:
+ Device* device;
+ GLuint vertexArrayObject;
+ };
+
+ class Queue : public QueueBase {
+ public:
+ Queue(Device* device, QueueBuilder* builder);
+
+ // NXT API
+ void Submit(uint32_t numCommands, CommandBuffer* const * commands);
+
+ private:
+ Device* device;
+ };
+
+}
+}
+
+#endif // BACKEND_OPENGL_OPENGLBACKEND_H_
diff --git a/src/backend/opengl/PipelineGL.cpp b/src/backend/opengl/PipelineGL.cpp
new file mode 100644
index 0000000..50b9f08
--- /dev/null
+++ b/src/backend/opengl/PipelineGL.cpp
@@ -0,0 +1,213 @@
+// 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 "PipelineGL.h"
+
+#include "OpenGLBackend.h"
+#include "PipelineLayoutGL.h"
+#include "ShaderModuleGL.h"
+
+#include <iostream>
+#include <set>
+
+namespace backend {
+namespace opengl {
+
+ namespace {
+
+ GLenum GLShaderType(nxt::ShaderStage stage) {
+ switch (stage) {
+ case nxt::ShaderStage::Vertex:
+ return GL_VERTEX_SHADER;
+ case nxt::ShaderStage::Fragment:
+ return GL_FRAGMENT_SHADER;
+ case nxt::ShaderStage::Compute:
+ return GL_COMPUTE_SHADER;
+ }
+ }
+
+ }
+
+ Pipeline::Pipeline(Device* device, PipelineBuilder* builder) : PipelineBase(builder), device(device) {
+ auto CreateShader = [](GLenum type, const char* source) -> GLuint {
+ GLuint shader = glCreateShader(type);
+ glShaderSource(shader, 1, &source, nullptr);
+ glCompileShader(shader);
+
+ GLint compileStatus = GL_FALSE;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus);
+ if (compileStatus == GL_FALSE) {
+ GLint infoLogLength = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
+
+ if (infoLogLength > 1) {
+ std::vector<char> buffer(infoLogLength);
+ glGetShaderInfoLog(shader, infoLogLength, nullptr, &buffer[0]);
+ std::cout << source << std::endl;
+ std::cout << "Program compilation failed:\n";
+ std::cout << buffer.data() << std::endl;
+ }
+ }
+ return shader;
+ };
+
+ auto FillPushConstants = [](const ShaderModule* module, GLPushConstantInfo* info, GLuint program) {
+ const auto& moduleInfo = module->GetPushConstants();
+ for (uint32_t i = 0; i < moduleInfo.names.size(); i++) {
+ (*info)[i] = -1;
+
+ unsigned int size = moduleInfo.sizes[i];
+ if (size == 0) {
+ continue;
+ }
+
+ GLint location = glGetUniformLocation(program, moduleInfo.names[i].c_str());
+ if (location == -1) {
+ continue;
+ }
+
+ for (uint32_t offset = 0; offset < size; offset++) {
+ (*info)[i + offset] = location + offset;
+ }
+ i += size - 1;
+ }
+ };
+
+ program = glCreateProgram();
+
+ for (auto stage : IterateStages(GetStageMask())) {
+ const ShaderModule* module = ToBackend(builder->GetStageInfo(stage).module.Get());
+
+ GLuint shader = CreateShader(GLShaderType(stage), module->GetSource());
+ glAttachShader(program, shader);
+ }
+
+ glLinkProgram(program);
+
+ GLint linkStatus = GL_FALSE;
+ glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+ if (linkStatus == GL_FALSE) {
+ GLint infoLogLength = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
+
+ if (infoLogLength > 1) {
+ std::vector<char> buffer(infoLogLength);
+ glGetProgramInfoLog(program, infoLogLength, nullptr, &buffer[0]);
+ std::cout << "Program link failed:\n";
+ std::cout << buffer.data() << std::endl;
+ }
+ }
+
+ for (auto stage : IterateStages(GetStageMask())) {
+ const ShaderModule* module = ToBackend(builder->GetStageInfo(stage).module.Get());
+ FillPushConstants(module, &glPushConstants[stage], program);
+ }
+
+ glUseProgram(program);
+
+ // The uniforms are part of the program state so we can pre-bind buffer units, texture units etc.
+ const auto& layout = ToBackend(GetLayout());
+ const auto& indices = layout->GetBindingIndexInfo();
+
+ for (uint32_t group = 0; group < kMaxBindGroups; ++group) {
+ const auto& groupInfo = layout->GetBindGroupLayout(group)->GetBindingInfo();
+
+ for (uint32_t binding = 0; binding < kMaxBindingsPerGroup; ++binding) {
+ if (!groupInfo.mask[binding]) {
+ continue;
+ }
+
+ std::string name = GetBindingName(group, binding);
+ switch (groupInfo.types[binding]) {
+ case nxt::BindingType::UniformBuffer:
+ {
+ GLint location = glGetUniformBlockIndex(program, name.c_str());
+ glUniformBlockBinding(program, location, indices[group][binding]);
+ }
+ break;
+
+ case nxt::BindingType::StorageBuffer:
+ {
+ GLuint location = glGetProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, name.c_str());
+ glShaderStorageBlockBinding(program, location, indices[group][binding]);
+ }
+ break;
+
+ case nxt::BindingType::Sampler:
+ case nxt::BindingType::SampledTexture:
+ // These binding types are handled in the separate sampler and texture emulation
+ break;
+
+ }
+ }
+ }
+
+ // Compute links between stages for combined samplers, then bind them to texture units
+ {
+ std::set<CombinedSampler> combinedSamplersSet;
+ for (auto stage : IterateStages(GetStageMask())) {
+ const auto& module = ToBackend(builder->GetStageInfo(stage).module);
+
+ for (const auto& combined : module->GetCombinedSamplerInfo()) {
+ combinedSamplersSet.insert(combined);
+ }
+ }
+
+ unitsForSamplers.resize(layout->GetNumSamplers());
+ unitsForTextures.resize(layout->GetNumSampledTextures());
+
+ GLuint textureUnit = layout->GetTextureUnitsUsed();
+ for (const auto& combined : combinedSamplersSet) {
+ std::string name = combined.GetName();
+ GLint location = glGetUniformLocation(program, name.c_str());
+ glUniform1i(location, textureUnit);
+
+ GLuint samplerIndex = indices[combined.samplerLocation.group][combined.samplerLocation.binding];
+ unitsForSamplers[samplerIndex].push_back(textureUnit);
+
+ GLuint textureIndex = indices[combined.textureLocation.group][combined.textureLocation.binding];
+ unitsForTextures[textureIndex].push_back(textureUnit);
+
+ textureUnit ++;
+ }
+ }
+ }
+
+ const Pipeline::GLPushConstantInfo& Pipeline::GetGLPushConstants(nxt::ShaderStage stage) const {
+ return glPushConstants[stage];
+ }
+
+ const std::vector<GLuint>& Pipeline::GetTextureUnitsForSampler(GLuint index) const {
+ ASSERT(index >= 0 && index < unitsForSamplers.size());
+ return unitsForSamplers[index];
+ }
+
+ const std::vector<GLuint>& Pipeline::GetTextureUnitsForTexture(GLuint index) const {
+ ASSERT(index >= 0 && index < unitsForSamplers.size());
+ return unitsForTextures[index];
+ }
+
+ GLuint Pipeline::GetProgramHandle() const {
+ return program;
+ }
+
+ void Pipeline::ApplyNow() {
+ glUseProgram(program);
+
+ auto inputState = ToBackend(GetInputState());
+ glBindVertexArray(inputState->GetVAO());
+ }
+
+}
+}
diff --git a/src/backend/opengl/PipelineGL.h b/src/backend/opengl/PipelineGL.h
new file mode 100644
index 0000000..ec0c0c5
--- /dev/null
+++ b/src/backend/opengl/PipelineGL.h
@@ -0,0 +1,55 @@
+// 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_PIPELINEGL_H_
+#define BACKEND_OPENGL_PIPELINEGL_H_
+
+#include "common/Pipeline.h"
+
+#include "glad/glad.h"
+
+#include <vector>
+
+namespace backend {
+namespace opengl {
+
+ class Device;
+ class ShaderModule;
+
+ class Pipeline : public PipelineBase {
+ public:
+ Pipeline(Device* device, PipelineBuilder* builder);
+
+ using GLPushConstantInfo = std::array<GLint, kMaxPushConstants>;
+ using BindingLocations = std::array<std::array<GLint, kMaxBindingsPerGroup>, kMaxBindGroups>;
+
+ const GLPushConstantInfo& GetGLPushConstants(nxt::ShaderStage stage) const;
+ const std::vector<GLuint>& GetTextureUnitsForSampler(GLuint index) const;
+ const std::vector<GLuint>& GetTextureUnitsForTexture(GLuint index) const;
+ GLuint GetProgramHandle() const;
+
+ void ApplyNow();
+
+ private:
+ GLuint program;
+ PerStage<GLPushConstantInfo> glPushConstants;
+ std::vector<std::vector<GLuint>> unitsForSamplers;
+ std::vector<std::vector<GLuint>> unitsForTextures;
+ Device* device;
+ };
+
+}
+}
+
+#endif // BACKEND_OPENGL_PIPELINEGL_H_
diff --git a/src/backend/opengl/PipelineLayoutGL.cpp b/src/backend/opengl/PipelineLayoutGL.cpp
new file mode 100644
index 0000000..1f3cb5e
--- /dev/null
+++ b/src/backend/opengl/PipelineLayoutGL.cpp
@@ -0,0 +1,80 @@
+// 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 "PipelineLayoutGL.h"
+
+#include "OpenGLBackend.h"
+
+namespace backend {
+namespace opengl {
+
+ PipelineLayout::PipelineLayout(Device* device, PipelineLayoutBuilder* builder)
+ : PipelineLayoutBase(builder), device(device) {
+ GLuint uboIndex = 0;
+ GLuint samplerIndex = 0;
+ GLuint sampledTextureIndex = 0;
+ GLuint ssboIndex = 0;
+
+ for (size_t group = 0; group < kMaxBindGroups; ++group) {
+ const auto& groupInfo = GetBindGroupLayout(group)->GetBindingInfo();
+
+ for (size_t binding = 0; binding < kMaxBindingsPerGroup; ++binding) {
+ if (!groupInfo.mask[binding]) {
+ continue;
+ }
+
+ switch (groupInfo.types[binding]) {
+ case nxt::BindingType::UniformBuffer:
+ indexInfo[group][binding] = uboIndex;
+ uboIndex ++;
+ break;
+ case nxt::BindingType::Sampler:
+ indexInfo[group][binding] = samplerIndex;
+ samplerIndex ++;
+ break;
+ case nxt::BindingType::SampledTexture:
+ indexInfo[group][binding] = sampledTextureIndex;
+ sampledTextureIndex ++;
+ break;
+
+ case nxt::BindingType::StorageBuffer:
+ indexInfo[group][binding] = ssboIndex;
+ ssboIndex ++;
+ break;
+ }
+ }
+ }
+
+ numSamplers = samplerIndex;
+ numSampledTextures = sampledTextureIndex;
+ }
+
+ const PipelineLayout::BindingIndexInfo& PipelineLayout::GetBindingIndexInfo() const {
+ return indexInfo;
+ }
+
+ GLuint PipelineLayout::GetTextureUnitsUsed() const {
+ return 0;
+ }
+
+ size_t PipelineLayout::GetNumSamplers() const {
+ return numSamplers;
+ }
+
+ size_t PipelineLayout::GetNumSampledTextures() const {
+ return numSampledTextures;
+ }
+
+}
+}
diff --git a/src/backend/opengl/PipelineLayoutGL.h b/src/backend/opengl/PipelineLayoutGL.h
new file mode 100644
index 0000000..fc35099
--- /dev/null
+++ b/src/backend/opengl/PipelineLayoutGL.h
@@ -0,0 +1,48 @@
+// 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_PIPELINELAYOUTGL_H_
+#define BACKEND_OPENGL_PIPELINELAYOUTGL_H_
+
+#include "common/PipelineLayout.h"
+
+#include "glad/glad.h"
+
+namespace backend {
+namespace opengl {
+
+ class Device;
+
+ class PipelineLayout : public PipelineLayoutBase {
+ public:
+ PipelineLayout(Device* device, PipelineLayoutBuilder* builder);
+
+ using BindingIndexInfo = std::array<std::array<GLuint, kMaxBindingsPerGroup>, kMaxBindGroups>;
+ const BindingIndexInfo& GetBindingIndexInfo() const;
+
+ GLuint GetTextureUnitsUsed() const;
+ size_t GetNumSamplers() const;
+ size_t GetNumSampledTextures() const;
+
+ private:
+ Device* device;
+ BindingIndexInfo indexInfo;
+ size_t numSamplers;
+ size_t numSampledTextures;
+ };
+
+}
+}
+
+#endif // BACKEND_OPENGL_PIPELINELAYOUTGL_H_
diff --git a/src/backend/opengl/SamplerGL.cpp b/src/backend/opengl/SamplerGL.cpp
new file mode 100644
index 0000000..73d418b
--- /dev/null
+++ b/src/backend/opengl/SamplerGL.cpp
@@ -0,0 +1,62 @@
+// 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 "SamplerGL.h"
+
+namespace backend {
+namespace opengl {
+
+ namespace {
+ GLenum MagFilterMode(nxt::FilterMode filter) {
+ switch (filter) {
+ case nxt::FilterMode::Nearest:
+ return GL_NEAREST;
+ case nxt::FilterMode::Linear:
+ return GL_LINEAR;
+ }
+ }
+
+ GLenum MinFilterMode(nxt::FilterMode minFilter, nxt::FilterMode mipMapFilter) {
+ switch (minFilter) {
+ case nxt::FilterMode::Nearest:
+ switch (mipMapFilter) {
+ case nxt::FilterMode::Nearest:
+ return GL_NEAREST_MIPMAP_NEAREST;
+ case nxt::FilterMode::Linear:
+ return GL_NEAREST_MIPMAP_LINEAR;
+ }
+ case nxt::FilterMode::Linear:
+ switch (mipMapFilter) {
+ case nxt::FilterMode::Nearest:
+ return GL_LINEAR_MIPMAP_NEAREST;
+ case nxt::FilterMode::Linear:
+ return GL_LINEAR_MIPMAP_LINEAR;
+ }
+ }
+ }
+ }
+
+ Sampler::Sampler(Device* device, SamplerBuilder* builder)
+ : SamplerBase(builder), device(device) {
+ glGenSamplers(1, &handle);
+ glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, MagFilterMode(builder->GetMagFilter()));
+ glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, MinFilterMode(builder->GetMinFilter(), builder->GetMipMapFilter()));
+ }
+
+ GLuint Sampler::GetHandle() const {
+ return handle;
+ }
+
+}
+}
diff --git a/src/backend/opengl/SamplerGL.h b/src/backend/opengl/SamplerGL.h
new file mode 100644
index 0000000..c238e8a
--- /dev/null
+++ b/src/backend/opengl/SamplerGL.h
@@ -0,0 +1,41 @@
+// 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_SAMPLERGL_H_
+#define BACKEND_OPENGL_SAMPLERGL_H_
+
+#include "common/Sampler.h"
+
+#include "glad/glad.h"
+
+namespace backend {
+namespace opengl {
+
+ class Device;
+
+ class Sampler : public SamplerBase {
+ public:
+ Sampler(Device* device, SamplerBuilder* builder);
+
+ GLuint GetHandle() const;
+
+ private:
+ Device* device;
+ GLuint handle;
+ };
+
+}
+}
+
+#endif // BACKEND_OPENGL_SAMPLERGL_H_
diff --git a/src/backend/opengl/ShaderModuleGL.cpp b/src/backend/opengl/ShaderModuleGL.cpp
new file mode 100644
index 0000000..afb5706
--- /dev/null
+++ b/src/backend/opengl/ShaderModuleGL.cpp
@@ -0,0 +1,105 @@
+// 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 "ShaderModuleGL.h"
+
+#include <spirv-cross/spirv_glsl.hpp>
+
+#include <sstream>
+
+namespace backend {
+namespace opengl {
+
+ std::string GetBindingName(uint32_t group, uint32_t binding) {
+ std::ostringstream o;
+ o << "nxt_binding_" << group << "_" << binding;
+ return o.str();
+ }
+
+ bool operator < (const BindingLocation& a, const BindingLocation& b) {
+ return std::tie(a.group, a.binding) < std::tie(b.group, b.binding);
+ }
+
+ bool operator < (const CombinedSampler& a, const CombinedSampler& b) {
+ return std::tie(a.samplerLocation, a.textureLocation) < std::tie(b.samplerLocation, b.textureLocation);
+ }
+
+ std::string CombinedSampler::GetName() const {
+ std::ostringstream o;
+ o << "nxt_combined";
+ o << "_" << samplerLocation.group << "_" << samplerLocation.binding;
+ o << "_with_" << textureLocation.group << "_" << textureLocation.binding;
+ return o.str();
+ }
+
+ ShaderModule::ShaderModule(Device* device, ShaderModuleBuilder* builder)
+ : ShaderModuleBase(builder), device(device) {
+ spirv_cross::CompilerGLSL compiler(builder->AcquireSpirv());
+ spirv_cross::CompilerGLSL::Options options;
+
+ // TODO(cwallez@chromium.org): discover the backing context version and use that.
+#if defined(__APPLE__)
+ options.version = 410;
+#else
+ options.version = 450;
+#endif
+ compiler.set_options(options);
+
+ ExtractSpirvInfo(compiler);
+
+ const auto& bindingInfo = GetBindingInfo();
+
+ // Extract bindings names so that it can be used to get its location in program.
+ // Now translate the separate sampler / textures into combined ones and store their info.
+ // We need to do this before removing the set and binding decorations.
+ compiler.build_combined_image_samplers();
+
+ for (const auto& combined : compiler.get_combined_image_samplers()) {
+ combinedInfo.emplace_back();
+
+ auto& info = combinedInfo.back();
+ info.samplerLocation.group = compiler.get_decoration(combined.sampler_id, spv::DecorationDescriptorSet);
+ info.samplerLocation.binding = compiler.get_decoration(combined.sampler_id, spv::DecorationBinding);
+ info.textureLocation.group = compiler.get_decoration(combined.image_id, spv::DecorationDescriptorSet);
+ info.textureLocation.binding = compiler.get_decoration(combined.image_id, spv::DecorationBinding);
+ compiler.set_name(combined.combined_id, info.GetName());
+ }
+
+ // Change binding names to be "nxt_binding_<group>_<binding>".
+ // Also unsets the SPIRV "Binding" decoration as it outputs "layout(binding=)" which
+ // isn't supported on OSX's OpenGL.
+ for (uint32_t group = 0; group < kMaxBindGroups; ++group) {
+ for (uint32_t binding = 0; binding < kMaxBindingsPerGroup; ++binding) {
+ const auto& info = bindingInfo[group][binding];
+ if (info.used) {
+ compiler.set_name(info.base_type_id, GetBindingName(group, binding));
+ compiler.unset_decoration(info.id, spv::DecorationBinding);
+ compiler.unset_decoration(info.id, spv::DecorationDescriptorSet);
+ }
+ }
+ }
+
+ glslSource = compiler.compile();
+ }
+
+ const char* ShaderModule::GetSource() const {
+ return reinterpret_cast<const char*>(glslSource.data());
+ }
+
+ const ShaderModule::CombinedSamplerInfo& ShaderModule::GetCombinedSamplerInfo() const {
+ return combinedInfo;
+ }
+
+}
+}
diff --git a/src/backend/opengl/ShaderModuleGL.h b/src/backend/opengl/ShaderModuleGL.h
new file mode 100644
index 0000000..d985527
--- /dev/null
+++ b/src/backend/opengl/ShaderModuleGL.h
@@ -0,0 +1,60 @@
+// 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_SHADERMODULEGL_H_
+#define BACKEND_OPENGL_SHADERMODULEGL_H_
+
+#include "common/ShaderModule.h"
+
+#include "glad/glad.h"
+
+namespace backend {
+namespace opengl {
+
+ class Device;
+
+ std::string GetBindingName(uint32_t group, uint32_t binding);
+
+ struct BindingLocation {
+ uint32_t group;
+ uint32_t binding;
+ };
+ bool operator < (const BindingLocation& a, const BindingLocation& b);
+
+ struct CombinedSampler {
+ BindingLocation samplerLocation;
+ BindingLocation textureLocation;
+ std::string GetName() const;
+ };
+ bool operator < (const CombinedSampler& a, const CombinedSampler& b);
+
+ class ShaderModule : public ShaderModuleBase {
+ public:
+ ShaderModule(Device* device, ShaderModuleBuilder* builder);
+
+ using CombinedSamplerInfo = std::vector<CombinedSampler>;
+
+ const char* GetSource() const;
+ const CombinedSamplerInfo& GetCombinedSamplerInfo() const;
+
+ private:
+ Device* device;
+ CombinedSamplerInfo combinedInfo;
+ std::string glslSource;
+ };
+
+}
+}
+
+#endif // BACKEND_OPENGL_SHADERMODULEGL_H_
diff --git a/src/backend/opengl/TextureGL.cpp b/src/backend/opengl/TextureGL.cpp
new file mode 100644
index 0000000..fc1a733
--- /dev/null
+++ b/src/backend/opengl/TextureGL.cpp
@@ -0,0 +1,86 @@
+// 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 "TextureGL.h"
+
+#include <algorithm>
+#include <vector>
+
+namespace backend {
+namespace opengl {
+
+ namespace {
+
+ GLenum TargetForDimension(nxt::TextureDimension dimension) {
+ switch (dimension) {
+ case nxt::TextureDimension::e2D:
+ return GL_TEXTURE_2D;
+ }
+ }
+
+ TextureFormatInfo GetGLFormatInfo(nxt::TextureFormat format) {
+ switch (format) {
+ case nxt::TextureFormat::R8G8B8A8Unorm:
+ return {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE};
+ }
+ }
+
+ }
+
+ // Texture
+
+ Texture::Texture(Device* device, TextureBuilder* builder)
+ : TextureBase(builder), device(device) {
+ target = TargetForDimension(GetDimension());
+
+ uint32_t width = GetWidth();
+ uint32_t height = GetHeight();
+ uint32_t levels = GetNumMipLevels();
+
+ auto formatInfo = GetGLFormatInfo(GetFormat());
+
+ glGenTextures(1, &handle);
+ glBindTexture(target, handle);
+
+ for (uint32_t i = 0; i < levels; ++i) {
+ glTexImage2D(target, i, formatInfo.internalFormat, width, height, 0, formatInfo.format, formatInfo.type, nullptr);
+ width = std::max(uint32_t(1), width / 2);
+ height = std::max(uint32_t(1), height / 2);
+ }
+
+ // The texture is not complete if it uses mipmapping and not all levels up to
+ // MAX_LEVEL have been defined.
+ glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels - 1);
+ }
+
+ GLuint Texture::GetHandle() const {
+ return handle;
+ }
+
+ GLenum Texture::GetGLTarget() const {
+ return target;
+ }
+
+ TextureFormatInfo Texture::GetGLFormat() const {
+ return GetGLFormatInfo(GetFormat());
+ }
+
+ // TextureView
+
+ TextureView::TextureView(Device* device, TextureViewBuilder* builder)
+ : TextureViewBase(builder), device(device) {
+ }
+
+}
+}
diff --git a/src/backend/opengl/TextureGL.h b/src/backend/opengl/TextureGL.h
new file mode 100644
index 0000000..6bdf534
--- /dev/null
+++ b/src/backend/opengl/TextureGL.h
@@ -0,0 +1,59 @@
+// 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_TEXTUREGL_H_
+#define BACKEND_OPENGL_TEXTUREGL_H_
+
+#include "common/Texture.h"
+
+#include "glad/glad.h"
+
+namespace backend {
+namespace opengl {
+
+ class Device;
+
+ struct TextureFormatInfo {
+ GLenum internalFormat;
+ GLenum format;
+ GLenum type;
+ };
+
+ class Texture : public TextureBase {
+ public:
+ Texture(Device* device, TextureBuilder* builder);
+
+ GLuint GetHandle() const;
+ GLenum GetGLTarget() const;
+ TextureFormatInfo GetGLFormat() const;
+
+ private:
+ Device* device;
+ GLuint handle;
+ GLenum target;
+ };
+
+ class TextureView : public TextureViewBase {
+ public:
+ TextureView(Device* device, TextureViewBuilder* builder);
+
+ private:
+ Device* device;
+ };
+
+
+}
+}
+
+#endif // BACKEND_OPENGL_TEXTUREGL_H_