diff options
Diffstat (limited to 'tools/slang-graphics/render-gl.cpp')
| -rw-r--r-- | tools/slang-graphics/render-gl.cpp | 1049 |
1 files changed, 1049 insertions, 0 deletions
diff --git a/tools/slang-graphics/render-gl.cpp b/tools/slang-graphics/render-gl.cpp new file mode 100644 index 000000000..f85a81ca4 --- /dev/null +++ b/tools/slang-graphics/render-gl.cpp @@ -0,0 +1,1049 @@ +// render-gl.cpp +#include "render-gl.h" + +//WORKING:#include "options.h" +#include "render.h" + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include "core/basic.h" +#include "core/secure-crt.h" +#include "external/stb/stb_image_write.h" + +#include "surface.h" + +// TODO(tfoley): eventually we should be able to run these +// tests on non-Windows targets to confirm that cross-compilation +// at least *works* on those platforms... +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include <Windows.h> +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +#ifdef _MSC_VER +#include <stddef.h> +#if (_MSC_VER < 1900) +#define snprintf sprintf_s +#endif +#endif + +#pragma comment(lib, "opengl32") + +#include <GL/GL.h> +#include "external/glext.h" + +// We define an "X-macro" for mapping over loadable OpenGL +// extension entry point that we will use, so that we can +// easily write generic code to iterate over them. +#define MAP_GL_EXTENSION_FUNCS(F) \ + F(glCreateProgram, PFNGLCREATEPROGRAMPROC) \ + F(glCreateShader, PFNGLCREATESHADERPROC) \ + F(glShaderSource, PFNGLSHADERSOURCEPROC) \ + F(glCompileShader, PFNGLCOMPILESHADERPROC) \ + F(glGetShaderiv, PFNGLGETSHADERIVPROC) \ + F(glDeleteShader, PFNGLDELETESHADERPROC) \ + F(glAttachShader, PFNGLATTACHSHADERPROC) \ + F(glLinkProgram, PFNGLLINKPROGRAMPROC) \ + F(glGetProgramiv, PFNGLGETPROGRAMIVPROC) \ + F(glGetProgramInfoLog, PFNGLGETPROGRAMINFOLOGPROC) \ + F(glDeleteProgram, PFNGLDELETEPROGRAMPROC) \ + F(glGetShaderInfoLog, PFNGLGETSHADERINFOLOGPROC) \ + F(glGenBuffers, PFNGLGENBUFFERSPROC) \ + F(glBindBuffer, PFNGLBINDBUFFERPROC) \ + F(glBufferData, PFNGLBUFFERDATAPROC) \ + F(glDeleteBuffers, PFNGLDELETEBUFFERSPROC) \ + F(glMapBuffer, PFNGLMAPBUFFERPROC) \ + F(glUnmapBuffer, PFNGLUNMAPBUFFERPROC) \ + F(glUseProgram, PFNGLUSEPROGRAMPROC) \ + F(glBindBufferBase, PFNGLBINDBUFFERBASEPROC) \ + F(glVertexAttribPointer, PFNGLVERTEXATTRIBPOINTERPROC) \ + F(glEnableVertexAttribArray, PFNGLENABLEVERTEXATTRIBARRAYPROC) \ + F(glDisableVertexAttribArray, PFNGLDISABLEVERTEXATTRIBARRAYPROC) \ + F(glDebugMessageCallback, PFNGLDEBUGMESSAGECALLBACKPROC) \ + F(glDispatchCompute, PFNGLDISPATCHCOMPUTEPROC) \ + F(glActiveTexture, PFNGLACTIVETEXTUREPROC) \ + F(glCreateSamplers, PFNGLCREATESAMPLERSPROC) \ + F(glDeleteSamplers, PFNGLDELETESAMPLERSPROC) \ + F(glBindSampler, PFNGLBINDSAMPLERPROC) \ + F(glTexImage3D, PFNGLTEXIMAGE3DPROC) \ + F(glSamplerParameteri, PFNGLSAMPLERPARAMETERIPROC) \ + /* end */ + +using namespace Slang; + +namespace slang_graphics { + +class GLRenderer : public Renderer +{ +public: + + // Renderer implementation + virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; + virtual void setClearColor(const float color[4]) override; + virtual void clearFrame() override; + virtual void presentFrame() override; + virtual TextureResource* createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData) override; + virtual BufferResource* createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData) override; + virtual SlangResult captureScreenSurface(Surface& surfaceOut) override; + virtual InputLayout* createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount) override; + virtual BindingState* createBindingState(const BindingState::Desc& bindingStateDesc) override; + virtual ShaderProgram* createProgram(const ShaderProgram::Desc& desc) override; + virtual void* map(BufferResource* buffer, MapFlavor flavor) override; + virtual void unmap(BufferResource* buffer) override; + virtual void setInputLayout(InputLayout* inputLayout) override; + virtual void setPrimitiveTopology(PrimitiveTopology topology) override; + virtual void setBindingState(BindingState* state); + virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; + virtual void setShaderProgram(ShaderProgram* inProgram) override; + virtual void draw(UInt vertexCount, UInt startVertex) override; + virtual void dispatchCompute(int x, int y, int z) override; + virtual void submitGpuWork() override {} + virtual void waitForGpu() override {} + virtual RendererType getRendererType() const override { return RendererType::OpenGl; } + + protected: + enum + { + kMaxVertexStreams = 16, + }; + + struct VertexAttributeFormat + { + GLint componentCount; + GLenum componentType; + GLboolean normalized; + }; + + struct VertexAttributeDesc + { + VertexAttributeFormat format; + GLuint streamIndex; + GLsizei offset; + }; + + class InputLayoutImpl: public InputLayout + { + public: + VertexAttributeDesc m_attributes[kMaxVertexStreams]; + UInt m_attributeCount = 0; + }; + + class BufferResourceImpl: public BufferResource + { + public: + typedef BufferResource Parent; + + BufferResourceImpl(Usage initialUsage, const Desc& desc, GLRenderer* renderer, GLuint id, GLenum target): + Parent(desc), + m_renderer(renderer), + m_handle(id), + m_initialUsage(initialUsage), + m_target(target) + {} + ~BufferResourceImpl() + { + if (m_renderer) + { + m_renderer->glDeleteBuffers(1, &m_handle); + } + } + + Usage m_initialUsage; + GLRenderer* m_renderer; + GLuint m_handle; + GLenum m_target; + }; + + class TextureResourceImpl: public TextureResource + { + public: + typedef TextureResource Parent; + + TextureResourceImpl(Usage initialUsage, const Desc& desc, GLRenderer* renderer): + Parent(desc), + m_initialUsage(initialUsage), + m_renderer(renderer) + { + m_target = 0; + m_handle = 0; + } + + ~TextureResourceImpl() + { + if (m_handle) + { + glDeleteTextures(1, &m_handle); + } + } + + Usage m_initialUsage; + GLRenderer* m_renderer; + GLenum m_target; + GLuint m_handle; + }; + + struct BindingDetail + { + GLuint m_samplerHandle = 0; + }; + + class BindingStateImpl: public BindingState + { + public: + typedef BindingState Parent; + + /// Ctor + BindingStateImpl(const Desc& desc, GLRenderer* renderer): + Parent(desc), + m_renderer(renderer) + { + } + + ~BindingStateImpl() + { + if (m_renderer) + { + m_renderer->destroyBindingEntries(getDesc(), m_bindingDetails.Buffer()); + } + } + + GLRenderer* m_renderer; + List<BindingDetail> m_bindingDetails; + }; + + class ShaderProgramImpl : public ShaderProgram + { + public: + ShaderProgramImpl(GLRenderer* renderer, GLuint id): + m_renderer(renderer), + m_id(id) + { + } + ~ShaderProgramImpl() + { + if (m_renderer) + { + m_renderer->glDeleteProgram(m_id); + } + } + + GLuint m_id; + GLRenderer* m_renderer; + }; + + enum class GlPixelFormat + { + Unknown, + RGBA_Unorm_UInt8, + CountOf, + }; + + struct GlPixelFormatInfo + { + GLint internalFormat; // such as GL_RGBA8 + GLenum format; // such as GL_RGBA + GLenum formatType; // such as GL_UNSIGNED_BYTE + }; + + void destroyBindingEntries(const BindingState::Desc& desc, const BindingDetail* details); + + void bindBufferImpl(int target, UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* offsets); + void flushStateForDraw(); + GLuint loadShader(GLenum stage, char const* source); + void debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message); + + /// Returns GlPixelFormat::Unknown if not an equivalent + static GlPixelFormat _getGlPixelFormat(Format format); + + static void APIENTRY staticDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam); + static VertexAttributeFormat getVertexAttributeFormat(Format format); + + static void compileTimeAsserts(); + + HDC m_hdc; + HGLRC m_glContext; + float m_clearColor[4] = { 0, 0, 0, 0 }; + + RefPtr<ShaderProgramImpl> m_boundShaderProgram; + RefPtr<InputLayoutImpl> m_boundInputLayout; + + GLenum m_boundPrimitiveTopology = GL_TRIANGLES; + GLuint m_boundVertexStreamBuffers[kMaxVertexStreams]; + UInt m_boundVertexStreamStrides[kMaxVertexStreams]; + UInt m_boundVertexStreamOffsets[kMaxVertexStreams]; + + Desc m_desc; + + // Declare a function pointer for each OpenGL + // extension function we need to load +#define DECLARE_GL_EXTENSION_FUNC(NAME, TYPE) TYPE NAME; + MAP_GL_EXTENSION_FUNCS(DECLARE_GL_EXTENSION_FUNC) +#undef DECLARE_GL_EXTENSION_FUNC + + static const GlPixelFormatInfo s_pixelFormatInfos[]; /// Maps GlPixelFormat to a format info +}; + +/* static */GLRenderer::GlPixelFormat GLRenderer::_getGlPixelFormat(Format format) +{ + switch (format) + { + case Format::RGBA_Unorm_UInt8: return GlPixelFormat::RGBA_Unorm_UInt8; + default: return GlPixelFormat::Unknown; + } +} + +/* static */ const GLRenderer::GlPixelFormatInfo GLRenderer::s_pixelFormatInfos[] = +{ + // internalType, format, formatType + { 0, 0, 0}, // GlPixelFormat::Unknown + { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }, // GlPixelFormat::RGBA_Unorm_UInt8 +}; + +/* static */void GLRenderer::compileTimeAsserts() +{ + SLANG_COMPILE_TIME_ASSERT(SLANG_COUNT_OF(s_pixelFormatInfos) == int(GlPixelFormat::CountOf)); +} + +Renderer* createGLRenderer() +{ + return new GLRenderer(); +} + +void GLRenderer::debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message) +{ + ::OutputDebugStringA("GL: "); + ::OutputDebugStringA(message); + ::OutputDebugStringA("\n"); + + switch (type) + { + case GL_DEBUG_TYPE_ERROR: + break; + default: + break; + } +} + +/* static */void APIENTRY GLRenderer::staticDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) +{ + ((GLRenderer*)userParam)->debugCallback(source, type, id, severity, length, message); +} + +/* static */GLRenderer::VertexAttributeFormat GLRenderer::getVertexAttributeFormat(Format format) +{ + switch (format) + { + default: assert(!"unexpected"); return VertexAttributeFormat(); + +#define CASE(NAME, COUNT, TYPE, NORMALIZED) \ + case Format::NAME: do { VertexAttributeFormat result = {COUNT, TYPE, NORMALIZED}; return result; } while (0) + + CASE(RGBA_Float32, 4, GL_FLOAT, GL_FALSE); + CASE(RGB_Float32, 3, GL_FLOAT, GL_FALSE); + CASE(RG_Float32, 2, GL_FLOAT, GL_FALSE); + CASE(R_Float32, 1, GL_FLOAT, GL_FALSE); +#undef CASE + } +} + +void GLRenderer::bindBufferImpl(int target, UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* offsets) +{ + for (UInt ii = 0; ii < slotCount; ++ii) + { + UInt slot = startSlot + ii; + + BufferResourceImpl* buffer = static_cast<BufferResourceImpl*>(buffers[ii]); + GLuint bufferID = buffer ? buffer->m_handle : 0; + + assert(!offsets || !offsets[ii]); + + glBindBufferBase(target, (GLuint)slot, bufferID); + } +} + +void GLRenderer::flushStateForDraw() +{ + auto layout = m_boundInputLayout.Ptr(); + auto attrCount = layout->m_attributeCount; + for (UInt ii = 0; ii < attrCount; ++ii) + { + auto& attr = layout->m_attributes[ii]; + + auto streamIndex = attr.streamIndex; + + glBindBuffer(GL_ARRAY_BUFFER, m_boundVertexStreamBuffers[streamIndex]); + + glVertexAttribPointer( + (GLuint)ii, + attr.format.componentCount, + attr.format.componentType, + attr.format.normalized, + (GLsizei)m_boundVertexStreamStrides[streamIndex], + (GLvoid*)(attr.offset + m_boundVertexStreamOffsets[streamIndex])); + + glEnableVertexAttribArray((GLuint)ii); + } + for (UInt ii = attrCount; ii < kMaxVertexStreams; ++ii) + { + glDisableVertexAttribArray((GLuint)ii); + } +} + +GLuint GLRenderer::loadShader(GLenum stage, const char* source) +{ + // GLSL is monumentally stupid. It officially requires the `#version` directive + // to be the first thing in the file, which wouldn't be so bad but the API + // doesn't provide a way to pass a `#define` into your shader other than by + // prepending it to the whole thing. + // + // We are going to solve this problem by doing some surgery on the source + // that was passed in. + + const char* sourceBegin = source; + const char* sourceEnd = source + strlen(source); + + // Look for a version directive in the user-provided source. + const char* versionBegin = strstr(source, "#version"); + const char* versionEnd = nullptr; + if (versionBegin) + { + // If we found a directive, then scan for the end-of-line + // after it, and use that to specify the slice. + versionEnd = strchr(versionBegin, '\n'); + if (!versionEnd) + { + versionEnd = sourceEnd; + } + else + { + versionEnd = versionEnd + 1; + } + } + else + { + // If we didn't find a directive, then treat it as being + // a zero-byte slice at the start of the string + versionBegin = sourceBegin; + versionEnd = sourceBegin; + } + + enum { kMaxSourceStringCount = 16 }; + const GLchar* sourceStrings[kMaxSourceStringCount]; + GLint sourceStringLengths[kMaxSourceStringCount]; + + int sourceStringCount = 0; + + const char* stagePrelude = "\n"; + switch (stage) + { +#define CASE(NAME) case GL_##NAME##_SHADER: stagePrelude = "#define __GLSL_" #NAME "__ 1\n"; break + + CASE(VERTEX); + CASE(TESS_CONTROL); + CASE(TESS_EVALUATION); + CASE(GEOMETRY); + CASE(FRAGMENT); + CASE(COMPUTE); + +#undef CASE + } + + const char* prelude = + "#define __GLSL__ 1\n" + ; + +#define ADD_SOURCE_STRING_SPAN(BEGIN, END) \ + sourceStrings[sourceStringCount] = BEGIN; \ + sourceStringLengths[sourceStringCount++] = GLint(END - BEGIN) \ + /* end */ + +#define ADD_SOURCE_STRING(BEGIN) \ + sourceStrings[sourceStringCount] = BEGIN; \ + sourceStringLengths[sourceStringCount++] = GLint(strlen(BEGIN)) \ + /* end */ + + ADD_SOURCE_STRING_SPAN(versionBegin, versionEnd); + ADD_SOURCE_STRING(stagePrelude); + ADD_SOURCE_STRING(prelude); + ADD_SOURCE_STRING_SPAN(sourceBegin, versionBegin); + ADD_SOURCE_STRING_SPAN(versionEnd, sourceEnd); + + auto shaderID = glCreateShader(stage); + glShaderSource( + shaderID, + sourceStringCount, + &sourceStrings[0], + &sourceStringLengths[0]); + glCompileShader(shaderID); + + GLint success = GL_FALSE; + glGetShaderiv(shaderID, GL_COMPILE_STATUS, &success); + if (!success) + { + int maxSize = 0; + glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &maxSize); + + auto infoBuffer = (char*)malloc(maxSize); + + int infoSize = 0; + glGetShaderInfoLog(shaderID, maxSize, &infoSize, infoBuffer); + if (infoSize > 0) + { + fprintf(stderr, "%s", infoBuffer); + ::OutputDebugStringA(infoBuffer); + } + + glDeleteShader(shaderID); + return 0; + } + + return shaderID; +} + +void GLRenderer::destroyBindingEntries(const BindingState::Desc& desc, const BindingDetail* details) +{ + const auto& bindings = desc.m_bindings; + const int numBindings = int(bindings.Count()); + for (int i = 0; i < numBindings; ++i) + { + const auto& binding = bindings[i]; + const auto& detail = details[i]; + + if (binding.bindingType == BindingType::Sampler && detail.m_samplerHandle != 0) + { + glDeleteSamplers(1, &detail.m_samplerHandle); + } + } +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! + +SlangResult GLRenderer::initialize(const Desc& desc, void* inWindowHandle) +{ + auto windowHandle = (HWND)inWindowHandle; + m_desc = desc; + + m_hdc = ::GetDC(windowHandle); + + PIXELFORMATDESCRIPTOR pixelFormatDesc = { sizeof(PIXELFORMATDESCRIPTOR) }; + pixelFormatDesc.nVersion = 1; + pixelFormatDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pixelFormatDesc.iPixelType = PFD_TYPE_RGBA; + pixelFormatDesc.cColorBits = 32; + pixelFormatDesc.cDepthBits = 24; + pixelFormatDesc.cStencilBits = 8; + pixelFormatDesc.iLayerType = PFD_MAIN_PLANE; + + int pixelFormatIndex = ChoosePixelFormat(m_hdc, &pixelFormatDesc); + SetPixelFormat(m_hdc, pixelFormatIndex, &pixelFormatDesc); + + m_glContext = wglCreateContext(m_hdc); + wglMakeCurrent(m_hdc, m_glContext); + + auto renderer = glGetString(GL_RENDERER); + auto extensions = glGetString(GL_EXTENSIONS); + + // Load each of our extension functions by name + +#define LOAD_GL_EXTENSION_FUNC(NAME, TYPE) NAME = (TYPE) wglGetProcAddress(#NAME); + MAP_GL_EXTENSION_FUNCS(LOAD_GL_EXTENSION_FUNC) +#undef LOAD_GL_EXTENSION_FUNC + + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + + glViewport(0, 0, desc.width, desc.height); + + if (glDebugMessageCallback) + { + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(staticDebugCallback, this); + } + + return SLANG_OK; +} + +void GLRenderer::setClearColor(const float color[4]) +{ + glClearColor(color[0], color[1], color[2], color[3]); +} + +void GLRenderer::clearFrame() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +} + +void GLRenderer::presentFrame() +{ + glFlush(); + ::SwapBuffers(m_hdc); +} + +SlangResult GLRenderer::captureScreenSurface(Surface& surfaceOut) +{ + SLANG_RETURN_ON_FAIL(surfaceOut.allocate(m_desc.width, m_desc.height, Format::RGBA_Unorm_UInt8, 1, SurfaceAllocator::getMallocAllocator())); + glReadPixels(0, 0, m_desc.width, m_desc.height, GL_RGBA, GL_UNSIGNED_BYTE, surfaceOut.m_data); + surfaceOut.flipInplaceVertically(); + return SLANG_OK; +} + +TextureResource* GLRenderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData) +{ + TextureResource::Desc srcDesc(descIn); + srcDesc.setDefaults(initialUsage); + + GlPixelFormat pixelFormat = _getGlPixelFormat(srcDesc.format); + if (pixelFormat == GlPixelFormat::Unknown) + { + return nullptr; + } + + const GlPixelFormatInfo& info = s_pixelFormatInfos[int(pixelFormat)]; + + const GLint internalFormat = info.internalFormat; + const GLenum format = info.format; + const GLenum formatType = info.formatType; + + RefPtr<TextureResourceImpl> texture(new TextureResourceImpl(initialUsage, srcDesc, this)); + + GLenum target = 0; + GLuint handle = 0; + glGenTextures(1, &handle); + + const int effectiveArraySize = srcDesc.calcEffectiveArraySize(); + + assert(initData); + assert(initData->numSubResources == srcDesc.numMipLevels * srcDesc.size.depth * effectiveArraySize); + + // Set on texture so will be freed if failure + texture->m_handle = handle; + const void*const*const data = initData->subResources; + + switch (srcDesc.type) + { + case Resource::Type::Texture1D: + { + if (srcDesc.arraySize > 0) + { + target = GL_TEXTURE_1D_ARRAY; + glBindTexture(target, handle); + + int slice = 0; + for (int i = 0; i < effectiveArraySize; i++) + { + for (int j = 0; j < srcDesc.numMipLevels; j++) + { + glTexImage2D(target, j, internalFormat, srcDesc.size.width, i, 0, format, formatType, data[slice++]); + } + } + } + else + { + target = GL_TEXTURE_1D; + glBindTexture(target, handle); + for (int i = 0; i < srcDesc.numMipLevels; i++) + { + glTexImage1D(target, i, internalFormat, srcDesc.size.width, 0, format, formatType, data[i]); + } + } + break; + } + case Resource::Type::TextureCube: + case Resource::Type::Texture2D: + { + if (srcDesc.arraySize > 0) + { + if (srcDesc.type == Resource::Type::TextureCube) + { + target = GL_TEXTURE_CUBE_MAP_ARRAY; + } + else + { + target = GL_TEXTURE_2D_ARRAY; + } + + glBindTexture(target, handle); + + int slice = 0; + for (int i = 0; i < effectiveArraySize; i++) + { + for (int j = 0; j < srcDesc.numMipLevels; j++) + { + glTexImage3D(target, j, internalFormat, srcDesc.size.width, srcDesc.size.height, slice, 0, format, formatType, data[slice++]); + } + } + } + else + { + if (srcDesc.type == Resource::Type::TextureCube) + { + target = GL_TEXTURE_CUBE_MAP; + glBindTexture(target, handle); + + int slice = 0; + for (int j = 0; j < 6; j++) + { + for (int i = 0; i < srcDesc.numMipLevels; i++) + { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, i, internalFormat, srcDesc.size.width, srcDesc.size.height, 0, format, formatType, data[slice++]); + } + } + } + else + { + target = GL_TEXTURE_2D; + glBindTexture(target, handle); + for (int i = 0; i < srcDesc.numMipLevels; i++) + { + glTexImage2D(target, i, internalFormat, srcDesc.size.width, srcDesc.size.height, 0, format, formatType, data[i]); + } + } + } + break; + } + case Resource::Type::Texture3D: + { + target = GL_TEXTURE_3D; + glBindTexture(target, handle); + for (int i = 0; i < srcDesc.numMipLevels; i++) + { + glTexImage3D(target, i, internalFormat, srcDesc.size.width, srcDesc.size.height, srcDesc.size.depth, 0, format, formatType, data[i]); + } + break; + } + default: return nullptr; + } + + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_REPEAT); + + // Assume regular sampling (might be superseded - if a combined sampler wanted) + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f); + + texture->m_target = target; + + return texture.detach(); +} + +static GLenum _calcUsage(Resource::Usage usage) +{ + typedef Resource::Usage Usage; + switch (usage) + { + case Usage::ConstantBuffer: return GL_DYNAMIC_DRAW; + default: return GL_STATIC_READ; + } +} + +static GLenum _calcTarget(Resource::Usage usage) +{ + typedef Resource::Usage Usage; + switch (usage) + { + case Usage::ConstantBuffer: return GL_UNIFORM_BUFFER; + default: return GL_SHADER_STORAGE_BUFFER; + } +} + +BufferResource* GLRenderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData) +{ + BufferResource::Desc desc(descIn); + desc.setDefaults(initialUsage); + + const GLenum target = _calcTarget(initialUsage); + // TODO: should derive from desc... + const GLenum usage = _calcUsage(initialUsage); + + GLuint bufferID = 0; + glGenBuffers(1, &bufferID); + glBindBuffer(target, bufferID); + + glBufferData(target, descIn.sizeInBytes, initData, usage); + + return new BufferResourceImpl(initialUsage, desc, this, bufferID, target); +} + +InputLayout* GLRenderer::createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount) +{ + InputLayoutImpl* inputLayout = new InputLayoutImpl; + + inputLayout->m_attributeCount = inputElementCount; + for (UInt ii = 0; ii < inputElementCount; ++ii) + { + auto& inputAttr = inputElements[ii]; + auto& glAttr = inputLayout->m_attributes[ii]; + + glAttr.streamIndex = 0; + glAttr.format = getVertexAttributeFormat(inputAttr.format); + glAttr.offset = (GLsizei)inputAttr.offset; + } + + return (InputLayout*)inputLayout; +} + +void* GLRenderer::map(BufferResource* bufferIn, MapFlavor flavor) +{ + BufferResourceImpl* buffer = static_cast<BufferResourceImpl*>(bufferIn); + + //GLenum target = GL_UNIFORM_BUFFER; + + GLuint access = 0; + switch (flavor) + { + case MapFlavor::WriteDiscard: + case MapFlavor::HostWrite: + access = GL_WRITE_ONLY; + break; + case MapFlavor::HostRead: + access = GL_READ_ONLY; + break; + } + + glBindBuffer(buffer->m_target, buffer->m_handle); + + return glMapBuffer(buffer->m_target, access); +} + +void GLRenderer::unmap(BufferResource* bufferIn) +{ + BufferResourceImpl* buffer = static_cast<BufferResourceImpl*>(bufferIn); + glUnmapBuffer(buffer->m_target); +} + +void GLRenderer::setInputLayout(InputLayout* inputLayout) +{ + m_boundInputLayout = static_cast<InputLayoutImpl*>(inputLayout); +} + +void GLRenderer::setPrimitiveTopology(PrimitiveTopology topology) +{ + GLenum glTopology = 0; + switch (topology) + { +#define CASE(NAME, VALUE) case PrimitiveTopology::NAME: glTopology = VALUE; break + + CASE(TriangleList, GL_TRIANGLES); + +#undef CASE + } + m_boundPrimitiveTopology = glTopology; +} + +void GLRenderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) +{ + for (UInt ii = 0; ii < slotCount; ++ii) + { + UInt slot = startSlot + ii; + + BufferResourceImpl* buffer = static_cast<BufferResourceImpl*>(buffers[ii]); + GLuint bufferID = buffer ? buffer->m_handle : 0; + + m_boundVertexStreamBuffers[slot] = bufferID; + m_boundVertexStreamStrides[slot] = strides[ii]; + m_boundVertexStreamOffsets[slot] = offsets[ii]; + } +} + +void GLRenderer::setShaderProgram(ShaderProgram* programIn) +{ + ShaderProgramImpl* program = static_cast<ShaderProgramImpl*>(programIn); + m_boundShaderProgram = program; + GLuint programID = program ? program->m_id : 0; + glUseProgram(programID); +} + +void GLRenderer::draw(UInt vertexCount, UInt startVertex = 0) +{ + flushStateForDraw(); + + glDrawArrays(m_boundPrimitiveTopology, (GLint)startVertex, (GLsizei)vertexCount); +} + +void GLRenderer::dispatchCompute(int x, int y, int z) +{ + glDispatchCompute(x, y, z); +} + +BindingState* GLRenderer::createBindingState(const BindingState::Desc& bindingStateDesc) +{ + RefPtr<BindingStateImpl> bindingState(new BindingStateImpl(bindingStateDesc, this)); + + const auto& srcBindings = bindingStateDesc.m_bindings; + const int numBindings = int(srcBindings.Count()); + + auto& dstDetails = bindingState->m_bindingDetails; + dstDetails.SetSize(numBindings); + + for (int i = 0; i < numBindings; ++i) + { + auto& dstDetail = dstDetails[i]; + const auto& srcBinding = srcBindings[i]; + + + switch (srcBinding.bindingType) + { + case BindingType::Texture: + case BindingType::Buffer: + { + break; + } + case BindingType::CombinedTextureSampler: + { + assert(srcBinding.resource && srcBinding.resource->isTexture()); + TextureResourceImpl* texture = static_cast<TextureResourceImpl*>(srcBinding.resource.Ptr()); + const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcBinding.descIndex]; + + if (samplerDesc.isCompareSampler) + { + auto target = texture->m_target; + + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + } + break; + } + case BindingType::Sampler: + { + const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcBinding.descIndex]; + + GLuint handle; + + glCreateSamplers(1, &handle); + glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, GL_REPEAT); + glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, GL_REPEAT); + glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, GL_REPEAT); + + if (samplerDesc.isCompareSampler) + { + glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + } + else + { + glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glSamplerParameteri(handle, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8); + } + + dstDetail.m_samplerHandle = handle; + break; + } + } + } + + return bindingState.detach(); +} + +void GLRenderer::setBindingState(BindingState* stateIn) +{ + BindingStateImpl* state = static_cast<BindingStateImpl*>(stateIn); + + const auto& bindingDesc = state->getDesc(); + + const auto& details = state->m_bindingDetails; + const auto& bindings = bindingDesc.m_bindings; + const int numBindings = int(bindings.Count()); + + for (int i = 0; i < numBindings; ++i) + { + const auto& binding = bindings[i]; + const auto& detail = details[i]; + + switch (binding.bindingType) + { + case BindingType::Buffer: + { + const int bindingIndex = binding.registerRange.getSingleIndex(); + + BufferResourceImpl* buffer = static_cast<BufferResourceImpl*>(binding.resource.Ptr()); + glBindBufferBase(buffer->m_target, bindingIndex, buffer->m_handle); + break; + } + case BindingType::Sampler: + { + for (int index = binding.registerRange.index; index < binding.registerRange.index + binding.registerRange.size; ++index) + { + glBindSampler(index, detail.m_samplerHandle); + } + break; + } + case BindingType::Texture: + case BindingType::CombinedTextureSampler: + { + BufferResourceImpl* buffer = static_cast<BufferResourceImpl*>(binding.resource.Ptr()); + + const int bindingIndex = binding.registerRange.getSingleIndex(); + + glActiveTexture(GL_TEXTURE0 + bindingIndex); + glBindTexture(buffer->m_target, buffer->m_handle); + break; + } + } + } +} + +ShaderProgram* GLRenderer::createProgram(const ShaderProgram::Desc& desc) +{ + auto programID = glCreateProgram(); + if(desc.pipelineType == PipelineType::Compute ) + { + auto computeKernel = desc.findKernel(StageType::Compute); + auto computeShaderID = loadShader(GL_COMPUTE_SHADER, (char const*) computeKernel->codeBegin); + glAttachShader(programID, computeShaderID); + glLinkProgram(programID); + glDeleteShader(computeShaderID); + } + else + { + auto vertexKernel = desc.findKernel(StageType::Vertex); + auto fragmentKernel = desc.findKernel(StageType::Fragment); + + auto vertexShaderID = loadShader(GL_VERTEX_SHADER, (char const*) vertexKernel->codeBegin); + auto fragmentShaderID = loadShader(GL_FRAGMENT_SHADER, (char const*) fragmentKernel->codeBegin); + + glAttachShader(programID, vertexShaderID); + glAttachShader(programID, fragmentShaderID); + + + glLinkProgram(programID); + + glDeleteShader(vertexShaderID); + glDeleteShader(fragmentShaderID); + } + GLint success = GL_FALSE; + glGetProgramiv(programID, GL_LINK_STATUS, &success); + if (!success) + { + int maxSize = 0; + glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &maxSize); + + auto infoBuffer = (char*)::malloc(maxSize); + + int infoSize = 0; + glGetProgramInfoLog(programID, maxSize, &infoSize, infoBuffer); + if (infoSize > 0) + { + fprintf(stderr, "%s", infoBuffer); + OutputDebugStringA(infoBuffer); + } + + ::free(infoBuffer); + + glDeleteProgram(programID); + return nullptr; + } + + return new ShaderProgramImpl(this, programID); +} + + +} // renderer_test |
