diff --git a/CMakeLists.txt b/CMakeLists.txt index 059e7dd..aa8769e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,10 @@ if(WIN32) lib/win/ApplicationWin32.cpp lib/win/WindowWin32.cpp lib/inputdev/HIDListenerWinUSB.cpp - lib/inputdev/HIDDeviceWinUSB.cpp) + lib/inputdev/HIDDeviceWinUSB.cpp + lib/graphicsdev/D3D11.cpp + lib/graphicsdev/D3D12.cpp + lib/graphicsdev/GLES3.cpp) list(APPEND _BOO_SYS_LIBS Winusb) elseif(APPLE) list(APPEND PLAT_SRCS @@ -18,7 +21,8 @@ elseif(APPLE) lib/mac/WindowCocoa.mm lib/mac/GLViewCocoa.mm lib/inputdev/HIDListenerIOKit.cpp - lib/inputdev/HIDDeviceIOKit.cpp) + lib/inputdev/HIDDeviceIOKit.cpp + lib/graphicsdev/GLES3.cpp) find_library(APPKIT_LIBRARY AppKit) find_library(IOKIT_LIBRARY IOKit) find_library(OPENGL_LIBRARY OpenGL) @@ -30,9 +34,9 @@ else() lib/x11/ApplicationWayland.hpp lib/x11/WindowXCB.cpp lib/x11/WindowWayland.cpp - lib/x11/GraphicsContextWayland.cpp lib/inputdev/HIDListenerUdev.cpp - lib/inputdev/HIDDeviceUdev.cpp) + lib/inputdev/HIDDeviceUdev.cpp + lib/graphicsdev/GLES3.cpp) find_package(PkgConfig) if(PKG_CONFIG_FOUND) @@ -94,6 +98,12 @@ add_library(Boo include/boo/inputdev/IHIDListener.hpp lib/inputdev/IHIDDevice.hpp include/boo/IGraphicsContext.hpp + include/boo/graphicsdev/IGraphicsDataFactory.hpp + include/boo/graphicsdev/IGraphicsCommandQueue.hpp + include/boo/graphicsdev/GLES3.hpp + include/boo/graphicsdev/D3D11.hpp + include/boo/graphicsdev/D3D12.hpp + include/boo/graphicsdev/Metal.hpp include/boo/IWindow.hpp include/boo/IApplication.hpp include/boo/System.hpp diff --git a/include/boo/IApplication.hpp b/include/boo/IApplication.hpp index 3ec2922..21a135f 100644 --- a/include/boo/IApplication.hpp +++ b/include/boo/IApplication.hpp @@ -14,7 +14,7 @@ class IApplication; struct IApplicationCallback { - virtual void appMain(IApplication*) {} + virtual int appMain(IApplication*) {} virtual void appQuitting(IApplication*) {} virtual void appFilesOpen(IApplication*, const std::vector&) {} }; @@ -51,7 +51,7 @@ public: virtual const std::vector& getArgs() const=0; /* Constructors/initializers for sub-objects */ - virtual IWindow* newWindow(const SystemString& title)=0; + virtual std::unique_ptr newWindow(const SystemString& title)=0; }; diff --git a/include/boo/IGraphicsContext.hpp b/include/boo/IGraphicsContext.hpp index 4ed606c..567cd76 100644 --- a/include/boo/IGraphicsContext.hpp +++ b/include/boo/IGraphicsContext.hpp @@ -1,8 +1,13 @@ #ifndef IGFXCONTEXT_HPP #define IGFXCONTEXT_HPP +#include +#include + namespace boo { +struct IGraphicsCommandQueue; +struct IGraphicsDataFactory; class IGraphicsContext { @@ -42,6 +47,9 @@ public: virtual void initializeContext()=0; virtual void makeCurrent()=0; + virtual std::unique_ptr createCommandQueue()=0; + virtual std::unique_ptr createDataFactory()=0; + }; } diff --git a/include/boo/IWindow.hpp b/include/boo/IWindow.hpp index 24efefd..bb2b501 100644 --- a/include/boo/IWindow.hpp +++ b/include/boo/IWindow.hpp @@ -134,7 +134,7 @@ public: virtual bool isFullscreen() const=0; virtual void setFullscreen(bool fs)=0; - virtual void waitForRetrace()=0; + virtual size_t waitForRetrace(size_t lastCount)=0; virtual uintptr_t getPlatformHandle() const=0; virtual void _incomingEvent(void* event) {(void)event;} diff --git a/include/boo/graphicsdev/D3D11.hpp b/include/boo/graphicsdev/D3D11.hpp new file mode 100644 index 0000000..2b3b8b8 --- /dev/null +++ b/include/boo/graphicsdev/D3D11.hpp @@ -0,0 +1,15 @@ +#ifndef GDEV_D3D11_HPP +#define GDEV_D3D11_HPP + +#include "IGraphicsDataFactory.hpp" + +namespace boo +{ + +class D3D11DataFactory : public IGraphicsDataFactory +{ +}; + +} + +#endif // GDEV_D3D11_HPP diff --git a/include/boo/graphicsdev/D3D12.hpp b/include/boo/graphicsdev/D3D12.hpp new file mode 100644 index 0000000..5cd068f --- /dev/null +++ b/include/boo/graphicsdev/D3D12.hpp @@ -0,0 +1,15 @@ +#ifndef GDEV_D3D12_HPP +#define GDEV_D3D12_HPP + +#include "IGraphicsDataFactory.hpp" + +namespace boo +{ + +class D3D12DataFactory : public IGraphicsDataFactory +{ +}; + +} + +#endif // GDEV_D3D12_HPP diff --git a/include/boo/graphicsdev/GLES3.hpp b/include/boo/graphicsdev/GLES3.hpp new file mode 100644 index 0000000..141651d --- /dev/null +++ b/include/boo/graphicsdev/GLES3.hpp @@ -0,0 +1,66 @@ +#ifndef GDEV_GLES3_HPP +#define GDEV_GLES3_HPP + +#include "IGraphicsDataFactory.hpp" +#include "IGraphicsCommandQueue.hpp" +#include +#include + +namespace boo +{ + +class GLES3VertexArray +{ + friend class GLES3DataFactory; + GLuint m_vao = 0; + bool initObjects(); + void clearObjects(); + GLES3VertexArray() = default; +public: + operator bool() const {return m_vao != 0;} + ~GLES3VertexArray() {clearObjects();} + GLES3VertexArray& operator=(const GLES3VertexArray&) = delete; + GLES3VertexArray(const GLES3VertexArray&) = delete; + GLES3VertexArray& operator=(GLES3VertexArray&& other) + { + m_vao = other.m_vao; + other.m_vao = 0; + return *this; + } + GLES3VertexArray(GLES3VertexArray&& other) {*this = std::move(other);} +}; + +class GLES3DataFactory : public IGraphicsDataFactory +{ + std::unique_ptr m_deferredData; +public: + GLES3DataFactory(); + + Platform platform() const {return PlatformOGLES3;} + const char* platformName() const {return "OpenGL ES 3.0";} + + const IGraphicsBufferS* newStaticBuffer(BufferUse use, const void* data, size_t sz); + IGraphicsBufferD* newDynamicBuffer(BufferUse use); + + GLES3VertexArray newVertexArray(size_t elementCount, const VertexElementDescriptor* elements); + + const ITextureS* newStaticTexture(size_t width, size_t height, size_t mips, TextureFormat fmt, + const void* data, size_t sz); + ITextureD* newDynamicTexture(size_t width, size_t height, TextureFormat fmt); + + const IShaderPipeline* newShaderPipeline(const char* vertSource, const char* fragSource, + BlendFactor srcFac, BlendFactor dstFac, + bool depthTest, bool depthWrite, bool backfaceCulling); + + const IShaderDataBinding* + newShaderDataBinding(const IShaderPipeline* pipeline, + size_t bufCount, const IGraphicsBuffer** bufs, + size_t texCount, const ITexture** texs); + + void reset(); + std::unique_ptr commit(); +}; + +} + +#endif // GDEV_GLES3_HPP diff --git a/include/boo/graphicsdev/IGraphicsCommandQueue.hpp b/include/boo/graphicsdev/IGraphicsCommandQueue.hpp new file mode 100644 index 0000000..064ceaa --- /dev/null +++ b/include/boo/graphicsdev/IGraphicsCommandQueue.hpp @@ -0,0 +1,39 @@ +#ifndef IGFXCOMMANDQUEUE_HPP +#define IGFXCOMMANDQUEUE_HPP + +#include "IGraphicsDataFactory.hpp" + +namespace boo +{ + +struct IGraphicsCommandQueue +{ + virtual ~IGraphicsCommandQueue() {} + + using Platform = IGraphicsDataFactory::Platform; + virtual Platform platform() const=0; + virtual const char* platformName() const=0; + + virtual void setShaderDataBinding(const IShaderDataBinding* binding)=0; + virtual void setRenderTarget(const ITextureD* target)=0; + + virtual void setClearColor(const float rgba[4])=0; + virtual void clearTarget(bool render=true, bool depth=true)=0; + + enum Primitive + { + PrimitiveTriangles, + TrimitiveTriStrips + }; + virtual void setDrawPrimitive(Primitive prim)=0; + virtual void draw(size_t start, size_t count)=0; + virtual void drawIndexed(size_t start, size_t count)=0; + virtual void drawInstances(size_t start, size_t count, size_t instCount)=0; + virtual void drawInstancesIndexed(size_t start, size_t count, size_t instCount)=0; + + virtual void execute()=0; +}; + +} + +#endif // IGFXCOMMANDQUEUE_HPP diff --git a/include/boo/graphicsdev/IGraphicsDataFactory.hpp b/include/boo/graphicsdev/IGraphicsDataFactory.hpp new file mode 100644 index 0000000..576c152 --- /dev/null +++ b/include/boo/graphicsdev/IGraphicsDataFactory.hpp @@ -0,0 +1,158 @@ +#ifndef IGFXDATAFACTORY_HPP +#define IGFXDATAFACTORY_HPP + +#include +#include + +namespace boo +{ + +struct IGraphicsBuffer +{ + bool dynamic() const {return m_dynamic;} +protected: + bool m_dynamic; + IGraphicsBuffer(bool dynamic) : m_dynamic(dynamic) {} +}; + +/** Static resource buffer for verts, indices, uniform constants */ +struct IGraphicsBufferS : IGraphicsBuffer +{ +protected: + IGraphicsBufferS() : IGraphicsBuffer(false) {} +}; + +/** Dynamic resource buffer for verts, indices, uniform constants */ +struct IGraphicsBufferD : IGraphicsBuffer +{ + virtual void load(const void* data, size_t sz)=0; + virtual void* map(size_t sz)=0; + virtual void unmap()=0; +protected: + IGraphicsBufferD() : IGraphicsBuffer(true) {} +}; + +struct ITexture +{ + bool dynamic() const {return m_dynamic;} +protected: + bool m_dynamic; + ITexture(bool dynamic) : m_dynamic(dynamic) {} +}; + +/** Static resource buffer for textures */ +struct ITextureS : ITexture +{ +protected: + ITextureS() : ITexture(false) {} +}; + +/** Dynamic resource buffer for textures */ +struct ITextureD : ITexture +{ + virtual void load(const void* data, size_t sz)=0; + virtual void* map(size_t sz)=0; + virtual void unmap()=0; +protected: + ITextureD() : ITexture(true) {} +}; + +/** Opaque token for referencing a complete graphics pipeline state necessary + * to rasterize geometry (shaders and blending modes mainly) */ +struct IShaderPipeline {}; + +/** Opaque token serving as indirection table for shader resources + * and IShaderPipeline reference. Each renderable surface-material holds one + * as a reference */ +struct IShaderDataBinding {}; + +/** Opaque token for maintaining ownership of factory-created resources + * deletion of this token triggers mass-deallocation of the factory's + * resource batch. */ +struct IGraphicsData +{ + virtual ~IGraphicsData() {} +}; + +/** Factory object for creating batches of resources as an IGraphicsData token */ +struct IGraphicsDataFactory +{ + virtual ~IGraphicsDataFactory() {} + + enum Platform + { + PlatformNull, + PlatformOGLES3, + PlatformD3D11, + PlatformD3D12, + PlatformMetal, + PlatformGX, + PlatformGX2 + }; + virtual Platform platform() const=0; + virtual const char* platformName() const=0; + + enum BufferUse + { + BufferUseNull, + BufferUseVertex, + BufferUseIndex, + BufferUseUniform + }; + virtual const IGraphicsBufferS* + newStaticBuffer(BufferUse use, const void* data, size_t sz)=0; + virtual IGraphicsBufferD* + newDynamicBuffer(BufferUse use)=0; + + struct VertexElementDescriptor + { + const IGraphicsBuffer* vertBuffer = nullptr; + const IGraphicsBuffer* indexBuffer = nullptr; + enum VertexSemantic + { + VertexSemanticPosition, + VertexSemanticNormal, + VertexSemanticColor, + VertexSemanticUV, + VertexSemanticWeight + } semantic; + }; + + enum TextureFormat + { + TextureFormatRGBA8, + TextureFormatDXT1, + TextureFormatPVRTC4 + }; + virtual const ITextureS* + newStaticTexture(size_t width, size_t height, size_t mips, TextureFormat fmt, + const void* data, size_t sz)=0; + virtual ITextureD* + newDynamicTexture(size_t width, size_t height, TextureFormat fmt)=0; + + enum BlendFactor + { + BlendFactorZero, + BlendFactorOne, + BlendFactorSrcColor, + BlendFactorInvSrcColor, + BlendFactorDstColor, + BlendFactorInvDstColor, + BlendFactorSrcAlpha, + BlendFactorInvSrcAlpha, + BlendFactorDstAlpha, + BlendFactorInvDstAlpha + }; + + virtual const IShaderDataBinding* + newShaderDataBinding(const IShaderPipeline* pipeline, + size_t bufCount, const IGraphicsBuffer** bufs, + size_t texCount, const ITexture** texs)=0; + + virtual void reset()=0; + virtual std::unique_ptr commit()=0; +}; + +} + +#endif // IGFXDATAFACTORY_HPP diff --git a/include/boo/graphicsdev/Metal.hpp b/include/boo/graphicsdev/Metal.hpp new file mode 100644 index 0000000..fea3bfe --- /dev/null +++ b/include/boo/graphicsdev/Metal.hpp @@ -0,0 +1,15 @@ +#ifndef GDEV_METAL_HPP +#define GDEV_METAL_HPP + +#include "IGraphicsDataFactory.hpp" + +namespace boo +{ + +class MetalDataFactory : public IGraphicsDataFactory +{ +}; + +} + +#endif // GDEV_METAL_HPP diff --git a/lib/graphicsdev/GLES3.cpp b/lib/graphicsdev/GLES3.cpp new file mode 100644 index 0000000..41336a8 --- /dev/null +++ b/lib/graphicsdev/GLES3.cpp @@ -0,0 +1,653 @@ +#include "boo/graphicsdev/GLES3.hpp" +#include "boo/IGraphicsContext.hpp" +#include +#include +#include +#include +#include +#include + +namespace boo +{ + +struct GLES3Data : IGraphicsData +{ + std::vector> m_SPs; + std::vector> m_SBufs; + std::vector> m_DBufs; + std::vector> m_STexs; + std::vector> m_DTexs; +}; + +static const GLenum USE_TABLE[] = +{ + GL_INVALID_ENUM, + GL_ARRAY_BUFFER, + GL_ELEMENT_ARRAY_BUFFER, + GL_UNIFORM_BUFFER +}; + +class GLES3GraphicsBufferS : IGraphicsBufferS +{ + friend class GLES3DataFactory; + GLuint m_buf; + GLenum m_target; + GLES3GraphicsBufferS(IGraphicsDataFactory::BufferUse use, const void* data, size_t sz) + { + m_target = USE_TABLE[use]; + glGenBuffers(1, &m_buf); + glBindBuffer(m_target, m_buf); + glBufferData(m_target, sz, data, GL_STATIC_DRAW); + } +public: + ~GLES3GraphicsBufferS() {glDeleteBuffers(1, &m_buf);} +}; + +class GLES3GraphicsBufferD : IGraphicsBufferD +{ + friend class GLES3DataFactory; + GLuint m_buf; + GLenum m_target; + void* m_mappedBuf = nullptr; + size_t m_mappedSize = 0; + GLES3GraphicsBufferD(IGraphicsDataFactory::BufferUse use) + { + m_target = USE_TABLE[use]; + glGenBuffers(1, &m_buf); + } +public: + ~GLES3GraphicsBufferD() {glDeleteBuffers(1, &m_buf);} + + void load(const void* data, size_t sz) + { + glBindBuffer(m_target, m_buf); + glBufferData(m_target, sz, data, GL_DYNAMIC_DRAW); + } + void* map(size_t sz) + { + if (m_mappedBuf) + free(m_mappedBuf); + m_mappedBuf = malloc(sz); + m_mappedSize = sz; + return m_mappedBuf; + } + void unmap() + { + glBindBuffer(m_target, m_buf); + glBufferData(m_target, m_mappedSize, m_mappedBuf, GL_DYNAMIC_DRAW); + free(m_mappedBuf); + m_mappedBuf = nullptr; + } +}; + +const IGraphicsBufferS* +GLES3DataFactory::newStaticBuffer(BufferUse use, const void* data, size_t sz) +{ + GLES3GraphicsBufferS* retval = new GLES3GraphicsBufferS(use, data, sz); + static_cast(m_deferredData.get())->m_SBufs.emplace_back(retval); + return retval; +} + +IGraphicsBufferD* +GLES3DataFactory::newDynamicBuffer(BufferUse use) +{ + GLES3GraphicsBufferD* retval = new GLES3GraphicsBufferD(use); + static_cast(m_deferredData.get())->m_DBufs.emplace_back(retval); + return retval; +} + +class GLES3TextureS : ITextureS +{ + friend class GLES3DataFactory; + GLuint m_tex; + GLES3TextureS(size_t width, size_t height, size_t mips, + IGraphicsDataFactory::TextureFormat fmt, + const void* data, size_t sz) + { + const uint8_t* dataIt = static_cast(data); + glGenTextures(1, &m_tex); + glBindTexture(GL_TEXTURE_2D, m_tex); + if (fmt == IGraphicsDataFactory::TextureFormatRGBA8) + { + for (size_t i=0 ; i(m_deferredData.get())->m_STexs.emplace_back(retval); + return retval; +} +ITextureD* +GLES3DataFactory::newDynamicTexture(size_t width, size_t height, TextureFormat fmt) +{ + GLES3TextureD* retval = new GLES3TextureD(width, height, fmt); + static_cast(m_deferredData.get())->m_DTexs.emplace_back(retval); + return retval; +} + +class GLES3ShaderPipeline : IShaderPipeline +{ + friend class GLES3DataFactory; + GLuint m_vert = 0; + GLuint m_frag = 0; + GLuint m_prog = 0; + GLenum m_sfactor = GL_ONE; + GLenum m_dfactor = GL_ZERO; + bool initObjects() + { + m_vert = glCreateShader(GL_VERTEX_SHADER); + m_frag = glCreateShader(GL_FRAGMENT_SHADER); + m_prog = glCreateProgram(); + if (!m_vert || !m_frag || !m_prog) + { + + glDeleteShader(m_vert); + m_vert = 0; + glDeleteShader(m_frag); + m_frag = 0; + glDeleteProgram(m_prog); + m_prog = 0; + return false; + } + glAttachShader(m_prog, m_vert); + glAttachShader(m_prog, m_frag); + return true; + } + void clearObjects() + { + if (m_vert) + glDeleteShader(m_vert); + if (m_frag) + glDeleteShader(m_frag); + if (m_prog) + glDeleteProgram(m_prog); + } + GLES3ShaderPipeline() = default; +public: + operator bool() const {return m_prog != 0;} + ~GLES3ShaderPipeline() {clearObjects();} + GLES3ShaderPipeline& operator=(const GLES3ShaderPipeline&) = delete; + GLES3ShaderPipeline(const GLES3ShaderPipeline&) = delete; + GLES3ShaderPipeline& operator=(GLES3ShaderPipeline&& other) + { + m_vert = other.m_vert; + other.m_vert = 0; + m_frag = other.m_frag; + other.m_frag = 0; + m_prog = other.m_prog; + other.m_prog = 0; + return *this; + } + GLES3ShaderPipeline(GLES3ShaderPipeline&& other) {*this = std::move(other);} +}; + +bool GLES3VertexArray::initObjects() +{ + glGenVertexArrays(1, &m_vao); + if (!m_vao) + return false; + return true; +} + +void GLES3VertexArray::clearObjects() +{ + glDeleteVertexArrays(1, &m_vao); + m_vao = 0; +} + +static const GLint SEMANTIC_COUNT_TABLE[] = +{ + 3, + 3, + 4, + 2, + 4 +}; + +static const size_t SEMANTIC_SIZE_TABLE[] = +{ + 12, + 12, + 4, + 8, + 16 +}; + +static const GLenum SEMANTIC_TYPE_TABLE[] = +{ + GL_FLOAT, + GL_FLOAT, + GL_UNSIGNED_BYTE, + GL_FLOAT, + GL_FLOAT +}; + +GLES3VertexArray GLES3DataFactory::newVertexArray +(size_t elementCount, const VertexElementDescriptor* elements) +{ + GLES3VertexArray vertArray; + if (!vertArray.initObjects()) + { + fprintf(stderr, "unable to create vertex array object\n"); + return vertArray; + } + + size_t stride = 0; + for (size_t i=0 ; isemantic]; + } + + size_t offset = 0; + glBindVertexArray(vertArray.m_vao); + const IGraphicsBuffer* lastVBO = nullptr; + const IGraphicsBuffer* lastEBO = nullptr; + for (size_t i=0 ; ivertBuffer != lastVBO) + { + lastVBO = desc->vertBuffer; + if (lastVBO->dynamic()) + { + const GLES3GraphicsBufferD* vbo = static_cast(lastVBO); + glBindBuffer(GL_ARRAY_BUFFER, vbo->m_buf); + } + else + { + const GLES3GraphicsBufferS* vbo = static_cast(lastVBO); + glBindBuffer(GL_ARRAY_BUFFER, vbo->m_buf); + } + } + if (desc->indexBuffer != lastEBO) + { + lastEBO = desc->indexBuffer; + if (lastEBO->dynamic()) + { + const GLES3GraphicsBufferD* ebo = static_cast(lastEBO); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo->m_buf); + } + else + { + const GLES3GraphicsBufferS* ebo = static_cast(lastEBO); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo->m_buf); + } + } + glVertexAttribPointer(i, SEMANTIC_COUNT_TABLE[desc->semantic], + SEMANTIC_TYPE_TABLE[desc->semantic], GL_TRUE, stride, (void*)offset); + offset += SEMANTIC_SIZE_TABLE[desc->semantic]; + } + + return vertArray; +} + +static const GLenum BLEND_FACTOR_TABLE[] = +{ + GL_ZERO, + GL_ONE, + GL_SRC_COLOR, + GL_ONE_MINUS_SRC_COLOR, + GL_DST_COLOR, + GL_ONE_MINUS_DST_COLOR, + GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, + GL_ONE_MINUS_DST_ALPHA +}; + +const IShaderPipeline* GLES3DataFactory::newShaderPipeline +(const char* vertSource, const char* fragSource, + BlendFactor srcFac, BlendFactor dstFac, + bool depthTest, bool depthWrite, bool backfaceCulling) +{ + GLES3ShaderPipeline shader; + if (!shader.initObjects()) + { + fprintf(stderr, "unable to create shader objects\n"); + return nullptr; + } + shader.m_sfactor = BLEND_FACTOR_TABLE[srcFac]; + shader.m_dfactor = BLEND_FACTOR_TABLE[dstFac]; + + glShaderSource(shader.m_vert, 1, &vertSource, nullptr); + glCompileShader(shader.m_vert); + GLint status; + glGetShaderiv(shader.m_vert, GL_COMPILE_STATUS, &status); + if (status != GL_TRUE) + { + GLint logLen; + glGetShaderiv(shader.m_vert, GL_INFO_LOG_LENGTH, &logLen); + char* log = (char*)malloc(logLen); + glGetShaderInfoLog(shader.m_vert, logLen, nullptr, log); + fprintf(stderr, "unable to compile vert source\n%s\n%s\n", log, vertSource); + free(log); + return nullptr; + } + + glShaderSource(shader.m_frag, 1, &fragSource, nullptr); + glCompileShader(shader.m_frag); + glGetShaderiv(shader.m_frag, GL_COMPILE_STATUS, &status); + if (status != GL_TRUE) + { + GLint logLen; + glGetShaderiv(shader.m_frag, GL_INFO_LOG_LENGTH, &logLen); + char* log = (char*)malloc(logLen); + glGetShaderInfoLog(shader.m_frag, logLen, nullptr, log); + fprintf(stderr, "unable to compile frag source\n%s\n%s\n", log, fragSource); + free(log); + return nullptr; + } + + glLinkProgram(shader.m_prog); + glGetProgramiv(shader.m_prog, GL_LINK_STATUS, &status); + if (status != GL_TRUE) + { + GLint logLen; + glGetProgramiv(shader.m_prog, GL_INFO_LOG_LENGTH, &logLen); + char* log = (char*)malloc(logLen); + glGetProgramInfoLog(shader.m_prog, logLen, nullptr, log); + fprintf(stderr, "unable to link shader program\n%s\n", log); + free(log); + return nullptr; + } + + GLES3ShaderPipeline* retval = new GLES3ShaderPipeline(std::move(shader)); + static_cast(m_deferredData.get())->m_SPs.emplace_back(retval); + return retval; +} + +struct GLES3ShaderDataBinding : IShaderDataBinding +{ + void bind() const + { + } +}; + +const IShaderDataBinding* +GLES3DataFactory::newShaderDataBinding(const IShaderPipeline* pipeline, + size_t bufCount, const IGraphicsBuffer** bufs, + size_t texCount, const ITexture** texs) +{ + return nullptr; +} + +GLES3DataFactory::GLES3DataFactory() +: m_deferredData(new struct GLES3Data()) {} + +void GLES3DataFactory::reset() +{ + m_deferredData.reset(new struct GLES3Data()); +} + +std::unique_ptr GLES3DataFactory::commit() +{ + std::unique_ptr retval(new struct GLES3Data()); + m_deferredData.swap(retval); + return retval; +} + +struct GLES3CommandQueue : IGraphicsCommandQueue +{ + Platform platform() const {return IGraphicsDataFactory::PlatformOGLES3;} + const char* platformName() const {return "OpenGL ES 3.0";} + IGraphicsContext& m_parent; + + struct Command + { + enum Op + { + OpSetShaderDataBinding, + OpSetRenderTarget, + OpSetClearColor, + OpClearTarget, + OpSetDrawPrimitive, + OpDraw, + OpDrawIndexed, + OpDrawInstances, + OpDrawInstancesIndexed + } m_op; + union + { + const IShaderDataBinding* binding; + const ITextureD* target; + float rgba[4]; + GLbitfield flags; + Primitive prim; + struct + { + size_t start; + size_t count; + size_t instCount; + }; + }; + Command(Op op) : m_op(op) {} + }; + std::vector m_cmdBufs[3]; + size_t m_fillBuf = 0; + size_t m_completeBuf = 0; + size_t m_drawBuf = 0; + bool m_running = true; + std::thread m_thr; + std::mutex m_mt; + std::condition_variable m_cv; + + static void RenderingWorker(GLES3CommandQueue* self) + { + self->m_parent.makeCurrent(); + while (self->m_running) + { + { + std::unique_lock lk(self->m_mt); + self->m_cv.wait(lk); + if (!self->m_running) + break; + self->m_drawBuf = self->m_completeBuf; + } + std::vector& cmds = self->m_cmdBufs[self->m_drawBuf]; + GLenum prim = GL_TRIANGLES; + for (const Command& cmd : cmds) + { + switch (cmd.m_op) + { + case Command::OpSetShaderDataBinding: + static_cast(cmd.binding)->bind(); + break; + case Command::OpSetRenderTarget: + { + const GLES3TextureD* tex = static_cast(cmd.target); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, tex->m_fbo); + break; + } + case Command::OpSetClearColor: + glClearColor(cmd.rgba[0], cmd.rgba[1], cmd.rgba[2], cmd.rgba[3]); + break; + case Command::OpClearTarget: + glClear(cmd.flags); + break; + case Command::OpSetDrawPrimitive: + if (cmd.prim == PrimitiveTriangles) + prim = GL_TRIANGLES; + else if (cmd.prim == TrimitiveTriStrips) + prim = GL_TRIANGLE_STRIP; + break; + case Command::OpDraw: + glDrawArrays(prim, cmd.start, cmd.count); + break; + case Command::OpDrawIndexed: + glDrawElements(prim, cmd.count, GL_UNSIGNED_INT, (void*)cmd.start); + break; + case Command::OpDrawInstances: + glDrawArraysInstanced(prim, cmd.start, cmd.count, cmd.instCount); + break; + case Command::OpDrawInstancesIndexed: + glDrawElementsInstanced(prim, cmd.count, GL_UNSIGNED_INT, (void*)cmd.start, cmd.instCount); + break; + default: break; + } + } + cmds.clear(); + } + } + + GLES3CommandQueue(IGraphicsContext& parent) + : m_parent(parent), + m_thr(RenderingWorker, this) {} + + ~GLES3CommandQueue() + { + m_running = false; + m_cv.notify_one(); + m_thr.join(); + } + + void setShaderDataBinding(const IShaderDataBinding* binding) + { + std::vector& cmds = m_cmdBufs[m_fillBuf]; + cmds.emplace_back(Command::OpSetShaderDataBinding); + cmds.back().binding = binding; + } + void setRenderTarget(const ITextureD* target) + { + std::vector& cmds = m_cmdBufs[m_fillBuf]; + cmds.emplace_back(Command::OpSetRenderTarget); + cmds.back().target = target; + } + + void setClearColor(const float rgba[4]) + { + std::vector& cmds = m_cmdBufs[m_fillBuf]; + cmds.emplace_back(Command::OpSetClearColor); + cmds.back().rgba[0] = rgba[0]; + cmds.back().rgba[1] = rgba[1]; + cmds.back().rgba[2] = rgba[2]; + cmds.back().rgba[3] = rgba[3]; + } + void clearTarget(bool render=true, bool depth=true) + { + std::vector& cmds = m_cmdBufs[m_fillBuf]; + cmds.emplace_back(Command::OpClearTarget); + cmds.back().flags = 0; + if (render) + cmds.back().flags |= GL_COLOR_BUFFER_BIT; + if (depth) + cmds.back().flags |= GL_DEPTH_BUFFER_BIT; + } + + void setDrawPrimitive(Primitive prim) + { + std::vector& cmds = m_cmdBufs[m_fillBuf]; + cmds.emplace_back(Command::OpSetDrawPrimitive); + cmds.back().prim = prim; + } + void draw(size_t start, size_t count) + { + std::vector& cmds = m_cmdBufs[m_fillBuf]; + cmds.emplace_back(Command::OpDraw); + cmds.back().start = start; + cmds.back().count = count; + } + void drawIndexed(size_t start, size_t count) + { + std::vector& cmds = m_cmdBufs[m_fillBuf]; + cmds.emplace_back(Command::OpDrawIndexed); + cmds.back().start = start; + cmds.back().count = count; + } + void drawInstances(size_t start, size_t count, size_t instCount) + { + std::vector& cmds = m_cmdBufs[m_fillBuf]; + cmds.emplace_back(Command::OpDrawInstances); + cmds.back().start = start; + cmds.back().count = count; + cmds.back().instCount = instCount; + } + void drawInstancesIndexed(size_t start, size_t count, size_t instCount) + { + std::vector& cmds = m_cmdBufs[m_fillBuf]; + cmds.emplace_back(Command::OpDrawInstancesIndexed); + cmds.back().start = start; + cmds.back().count = count; + cmds.back().instCount = instCount; + } + + void execute() + { + std::unique_lock lk(m_mt); + m_completeBuf = m_fillBuf; + for (size_t i=0 ; i<3 ; ++i) + { + if (i == m_completeBuf || i == m_drawBuf) + continue; + m_fillBuf = i; + break; + } + lk.unlock(); + m_cv.notify_one(); + m_cmdBufs[m_fillBuf].clear(); + } +}; + +IGraphicsCommandQueue* _NewGLES3CommandQueue(IGraphicsContext& parent) +{ + return new struct GLES3CommandQueue(parent); +} + +} diff --git a/lib/x11/ApplicationWayland.hpp b/lib/x11/ApplicationWayland.hpp index 9e7135d..62693bf 100644 --- a/lib/x11/ApplicationWayland.hpp +++ b/lib/x11/ApplicationWayland.hpp @@ -71,9 +71,9 @@ public: return m_args; } - IWindow* newWindow(const std::string& title) + std::unique_ptr newWindow(const std::string& title) { - return _WindowWaylandNew(title); + return std::unique_ptr(_WindowWaylandNew(title)); } }; diff --git a/lib/x11/ApplicationXCB.hpp b/lib/x11/ApplicationXCB.hpp index 71a7986..51d52e8 100644 --- a/lib/x11/ApplicationXCB.hpp +++ b/lib/x11/ApplicationXCB.hpp @@ -21,6 +21,7 @@ DBusConnection* RegisterDBus(const char* appName, bool& isFirst); #include +#include namespace boo { @@ -253,56 +254,69 @@ public: if (!m_xcbConn) return 1; - xcb_generic_event_t* event; - fd_set fds; - FD_ZERO(&fds); - FD_SET(m_xcbFd, &fds); - FD_SET(m_dbusFd, &fds); - select(m_maxFd+1, &fds, NULL, NULL, NULL); + /* Spawn client thread */ + int clientReturn = 0; + std::thread clientThread([&]() + {clientReturn = m_callback.appMain(this);}); - if (FD_ISSET(m_xcbFd, &fds)) + /* Begin application event loop */ + while (true) { - event = xcb_poll_for_event(m_xcbConn); - if (event) - { - bool windowEvent; - xcb_window_t evWindow = GetWindowOfEvent(event, windowEvent); - //fprintf(stderr, "EVENT %d\n", XCB_EVENT_RESPONSE_TYPE(event)); - if (windowEvent) - { - auto window = m_windows.find(evWindow); - if (window != m_windows.end()) - window->second->_incomingEvent(event); - } - free(event); - } - } + xcb_generic_event_t* event; + fd_set fds; + FD_ZERO(&fds); + FD_SET(m_xcbFd, &fds); + FD_SET(m_dbusFd, &fds); + select(m_maxFd+1, &fds, NULL, NULL, NULL); - if (FD_ISSET(m_dbusFd, &fds)) - { - DBusMessage* msg; - dbus_connection_read_write(m_dbus, 0); - while ((msg = dbus_connection_pop_message(m_dbus))) + if (FD_ISSET(m_xcbFd, &fds)) { - /* check if the message is a signal from the correct interface and with the correct name */ - if (dbus_message_is_signal(msg, "boo.signal.FileHandling", "Open")) + event = xcb_poll_for_event(m_xcbConn); + if (event) { - /* read the parameters */ - std::vector paths; - DBusMessageIter iter; - dbus_message_iter_init(msg, &iter); - while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) + bool windowEvent; + xcb_window_t evWindow = GetWindowOfEvent(event, windowEvent); + //fprintf(stderr, "EVENT %d\n", XCB_EVENT_RESPONSE_TYPE(event)); + if (windowEvent) { - const char* argVal; - dbus_message_iter_get_basic(&iter, &argVal); - paths.push_back(argVal); - dbus_message_iter_next(&iter); + auto window = m_windows.find(evWindow); + if (window != m_windows.end()) + window->second->_incomingEvent(event); } - m_callback.appFilesOpen(this, paths); + free(event); + } + } + + if (FD_ISSET(m_dbusFd, &fds)) + { + DBusMessage* msg; + dbus_connection_read_write(m_dbus, 0); + while ((msg = dbus_connection_pop_message(m_dbus))) + { + /* check if the message is a signal from the correct interface and with the correct name */ + if (dbus_message_is_signal(msg, "boo.signal.FileHandling", "Open")) + { + /* read the parameters */ + std::vector paths; + DBusMessageIter iter; + dbus_message_iter_init(msg, &iter); + while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) + { + const char* argVal; + dbus_message_iter_get_basic(&iter, &argVal); + paths.push_back(argVal); + dbus_message_iter_next(&iter); + } + m_callback.appFilesOpen(this, paths); + } + dbus_message_unref(msg); } - dbus_message_unref(msg); } } + + m_callback.appQuitting(this); + clientThread.join(); + return clientReturn; } const std::string& getUniqueName() const @@ -325,11 +339,11 @@ public: return m_args; } - IWindow* newWindow(const std::string& title) + std::unique_ptr newWindow(const std::string& title) { IWindow* newWindow = _WindowXCBNew(title, m_xcbConn); m_windows[(xcb_window_t)newWindow->getPlatformHandle()] = newWindow; - return newWindow; + return std::unique_ptr(newWindow); } }; diff --git a/lib/x11/GraphicsContextWayland.cpp b/lib/x11/GraphicsContextWayland.cpp deleted file mode 100644 index 4466cb2..0000000 --- a/lib/x11/GraphicsContextWayland.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "boo/IGraphicsContext.hpp" -#include "boo/IWindow.hpp" - -namespace boo -{ - -struct GraphicsContextWayland : IGraphicsContext -{ - - EGraphicsAPI m_api; - EPixelFormat m_pf; - IWindow* m_parentWindow; - -public: - IWindowCallback* m_callback; - - GraphicsContextWayland(EGraphicsAPI api, IWindow* parentWindow) - : m_api(api), - m_pf(PF_RGBA8), - m_parentWindow(parentWindow) - {} - - ~GraphicsContextWayland() - { - - } - - void _setCallback(IWindowCallback* cb) - { - m_callback = cb; - } - - EGraphicsAPI getAPI() const - { - return m_api; - } - - EPixelFormat getPixelFormat() const - { - return m_pf; - } - - void setPixelFormat(EPixelFormat pf) - { - if (pf > PF_RGBAF32_Z24) - return; - m_pf = pf; - } - - void initializeContext() - { - - } - - void makeCurrent() - { - } - -}; - -IGraphicsContext* _GraphicsContextWaylandNew(IGraphicsContext::EGraphicsAPI api, - IWindow* parentWindow) -{ - return new GraphicsContextWayland(api, parentWindow); -} - -} diff --git a/lib/x11/WindowWayland.cpp b/lib/x11/WindowWayland.cpp index b3815b4..172ac5d 100644 --- a/lib/x11/WindowWayland.cpp +++ b/lib/x11/WindowWayland.cpp @@ -11,8 +11,59 @@ namespace boo extern PFNGLXGETVIDEOSYNCSGIPROC FglXGetVideoSyncSGI; extern PFNGLXWAITVIDEOSYNCSGIPROC FglXWaitVideoSyncSGI; -IGraphicsContext* _GraphicsContextWaylandNew(IGraphicsContext::EGraphicsAPI api, - IWindow* parentWindow); +struct GraphicsContextWayland : IGraphicsContext +{ + + EGraphicsAPI m_api; + EPixelFormat m_pf; + IWindow* m_parentWindow; + +public: + IWindowCallback* m_callback; + + GraphicsContextWayland(EGraphicsAPI api, IWindow* parentWindow) + : m_api(api), + m_pf(PF_RGBA8), + m_parentWindow(parentWindow) + {} + + ~GraphicsContextWayland() + { + + } + + void _setCallback(IWindowCallback* cb) + { + m_callback = cb; + } + + EGraphicsAPI getAPI() const + { + return m_api; + } + + EPixelFormat getPixelFormat() const + { + return m_pf; + } + + void setPixelFormat(EPixelFormat pf) + { + if (pf > PF_RGBAF32_Z24) + return; + m_pf = pf; + } + + void initializeContext() + { + + } + + void makeCurrent() + { + } + +}; struct WindowWayland : IWindow { @@ -81,10 +132,11 @@ struct WindowWayland : IWindow } - void waitForRetrace() + size_t waitForRetrace(size_t count) { unsigned int sync; FglXWaitVideoSyncSGI(1, 0, &sync); + return 0; } uintptr_t getPlatformHandle() const diff --git a/lib/x11/WindowXCB.cpp b/lib/x11/WindowXCB.cpp index 33dd22f..a45baf7 100644 --- a/lib/x11/WindowXCB.cpp +++ b/lib/x11/WindowXCB.cpp @@ -1,6 +1,7 @@ #include "boo/IWindow.hpp" #include "boo/IGraphicsContext.hpp" #include "boo/IApplication.hpp" +#include "boo/graphicsdev/GLES3.hpp" #include #include @@ -27,6 +28,7 @@ namespace boo { +IGraphicsCommandQueue* _NewGLES3CommandQueue(IGraphicsContext& parent); extern PFNGLXGETVIDEOSYNCSGIPROC FglXGetVideoSyncSGI; extern PFNGLXWAITVIDEOSYNCSGIPROC FglXWaitVideoSyncSGI; @@ -293,6 +295,16 @@ public: free(reply); } + std::unique_ptr createCommandQueue() + { + return std::unique_ptr(_NewGLES3CommandQueue(*this)); + } + + std::unique_ptr createDataFactory() + { + return std::unique_ptr(new struct GLES3DataFactory()); + } + }; struct WindowXCB : IWindow @@ -425,6 +437,7 @@ public: void showWindow() { xcb_map_window(m_xcbConn, m_windowId); + m_gfxCtx.makeCurrent(); xcb_flush(m_xcbConn); } @@ -530,10 +543,11 @@ public: (const char*)&fsEvent); } - void waitForRetrace() + size_t waitForRetrace(size_t count) { unsigned int sync; FglXWaitVideoSyncSGI(1, 0, &sync); + return 0; } uintptr_t getPlatformHandle() const diff --git a/test/main.cpp b/test/main.cpp index bd82c57..cfe837c 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -169,19 +169,29 @@ struct CTestWindowCallback : IWindowCallback struct TestApplicationCallback : IApplicationCallback { - IWindow* mainWindow = NULL; + std::unique_ptr mainWindow; boo::TestDeviceFinder devFinder; CTestWindowCallback windowCallback; - void appMain(IApplication* app) + bool running = true; + int appMain(IApplication* app) { mainWindow = app->newWindow(_S("YAY!")); mainWindow->setCallback(&windowCallback); mainWindow->showWindow(); devFinder.startScanning(); + + size_t retraceCount = 0; + while (running) + { + retraceCount = mainWindow->waitForRetrace(retraceCount); + + } + + return 0; } void appQuitting(IApplication*) { - delete mainWindow; + running = false; } void appFilesOpen(IApplication*, const std::vector& paths) {