summaryrefslogtreecommitdiffstats
path: root/tools/slang-graphics/render-gl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/slang-graphics/render-gl.cpp')
-rw-r--r--tools/slang-graphics/render-gl.cpp1049
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