Initial commit of all the NXT integration.

More like squashed history, contributors were:
 - Kai Ninomiya
 - Corentin Wallez
This commit is contained in:
Corentin Wallez
2017-04-20 14:38:20 -04:00
commit f07e3bd4c9
134 changed files with 24658 additions and 0 deletions

View File

@@ -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);
}
}
}

View File

@@ -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_

View File

@@ -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"

View File

@@ -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();
}
}
}
}

View File

@@ -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_

View File

@@ -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());
}
}
}

View File

@@ -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_

View File

@@ -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;
}
}
}

View File

@@ -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_

View File

@@ -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;
}
}
}

View File

@@ -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_

View File

@@ -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;
}
}
}

View File

@@ -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_

View File

@@ -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) {
}
}
}

View File

@@ -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_