diff --git a/Source/Core/Rendering/RenderDevice.cs b/Source/Core/Rendering/RenderDevice.cs index f31bf039..5c5b89b7 100755 --- a/Source/Core/Rendering/RenderDevice.cs +++ b/Source/Core/Rendering/RenderDevice.cs @@ -29,6 +29,11 @@ using System.Reflection; namespace CodeImp.DoomBuilder.Rendering { + public class RenderDeviceException : Exception + { + public RenderDeviceException(string message) : base(message) { } + } + public class RenderDevice : IDisposable { public RenderDevice(RenderTargetControl rendertarget) @@ -59,6 +64,13 @@ namespace CodeImp.DoomBuilder.Rendering public bool Disposed { get { return Handle == IntPtr.Zero; } } + private void CheckAndThrow() + { + string err = Marshal.PtrToStringAnsi(RenderDevice_GetError(Handle)); + if (err != "") + throw new RenderDeviceException(err); + } + public void Dispose() { if (!Disposed) @@ -71,36 +83,43 @@ namespace CodeImp.DoomBuilder.Rendering public void SetShader(ShaderName shader) { RenderDevice_SetShader(Handle, shader); + CheckAndThrow(); } public void SetUniform(UniformName uniform, bool value) { RenderDevice_SetUniform(Handle, uniform, new float[] { value ? 1.0f : 0.0f }, 1); + CheckAndThrow(); } public void SetUniform(UniformName uniform, float value) { RenderDevice_SetUniform(Handle, uniform, new float[] { value }, 1); + CheckAndThrow(); } public void SetUniform(UniformName uniform, Vector2 value) { RenderDevice_SetUniform(Handle, uniform, new float[] { value.X, value.Y }, 2); + CheckAndThrow(); } public void SetUniform(UniformName uniform, Vector3 value) { RenderDevice_SetUniform(Handle, uniform, new float[] { value.X, value.Y, value.Z }, 3); + CheckAndThrow(); } public void SetUniform(UniformName uniform, Vector4 value) { RenderDevice_SetUniform(Handle, uniform, new float[] { value.X, value.Y, value.Z, value.W }, 4); + CheckAndThrow(); } public void SetUniform(UniformName uniform, Color4 value) { RenderDevice_SetUniform(Handle, uniform, new float[] { value.Red, value.Green, value.Blue, value.Alpha }, 4); + CheckAndThrow(); } public void SetUniform(UniformName uniform, Matrix matrix) @@ -111,173 +130,207 @@ namespace CodeImp.DoomBuilder.Rendering matrix.M31, matrix.M32, matrix.M33, matrix.M34, matrix.M41, matrix.M42, matrix.M43, matrix.M44 }, 16); + CheckAndThrow(); } public void SetVertexBuffer(VertexBuffer buffer) { RenderDevice_SetVertexBuffer(Handle, buffer != null ? buffer.Handle : IntPtr.Zero); + CheckAndThrow(); } public void SetIndexBuffer(IndexBuffer buffer) { RenderDevice_SetIndexBuffer(Handle, buffer != null ? buffer.Handle : IntPtr.Zero); + CheckAndThrow(); } public void SetAlphaBlendEnable(bool value) { RenderDevice_SetAlphaBlendEnable(Handle, value); + CheckAndThrow(); } public void SetAlphaTestEnable(bool value) { RenderDevice_SetAlphaTestEnable(Handle, value); + CheckAndThrow(); } public void SetCullMode(Cull mode) { RenderDevice_SetCullMode(Handle, mode); + CheckAndThrow(); } public void SetBlendOperation(BlendOperation op) { RenderDevice_SetBlendOperation(Handle, op); + CheckAndThrow(); } public void SetSourceBlend(Blend blend) { RenderDevice_SetSourceBlend(Handle, blend); + CheckAndThrow(); } public void SetDestinationBlend(Blend blend) { RenderDevice_SetDestinationBlend(Handle, blend); + CheckAndThrow(); } public void SetFillMode(FillMode mode) { RenderDevice_SetFillMode(Handle, mode); + CheckAndThrow(); } public void SetMultisampleAntialias(bool value) { RenderDevice_SetMultisampleAntialias(Handle, value); + CheckAndThrow(); } public void SetZEnable(bool value) { RenderDevice_SetZEnable(Handle, value); + CheckAndThrow(); } public void SetZWriteEnable(bool value) { RenderDevice_SetZWriteEnable(Handle, value); + CheckAndThrow(); } public void SetTexture(BaseTexture value) { RenderDevice_SetTexture(Handle, value != null ? value.Handle : IntPtr.Zero); + CheckAndThrow(); } public void SetSamplerFilter(TextureFilter filter) { SetSamplerFilter(filter, filter, TextureFilter.None, 0.0f); + CheckAndThrow(); } public void SetSamplerFilter(TextureFilter minfilter, TextureFilter magfilter, TextureFilter mipfilter, float maxanisotropy) { RenderDevice_SetSamplerFilter(Handle, minfilter, magfilter, mipfilter, maxanisotropy); + CheckAndThrow(); } public void SetSamplerState(TextureAddress address) { SetSamplerState(address, address, address); + CheckAndThrow(); } public void SetSamplerState(TextureAddress addressU, TextureAddress addressV, TextureAddress addressW) { RenderDevice_SetSamplerState(Handle, addressU, addressV, addressW); + CheckAndThrow(); } public void DrawIndexed(PrimitiveType type, int startIndex, int primitiveCount) { RenderDevice_DrawIndexed(Handle, type, startIndex, primitiveCount); + CheckAndThrow(); } public void Draw(PrimitiveType type, int startIndex, int primitiveCount) { RenderDevice_Draw(Handle, type, startIndex, primitiveCount); + CheckAndThrow(); } public void Draw(PrimitiveType type, int startIndex, int primitiveCount, FlatVertex[] data) { RenderDevice_DrawData(Handle, type, startIndex, primitiveCount, data); + CheckAndThrow(); } public void StartRendering(bool clear, Color4 backcolor) { RenderDevice_StartRendering(Handle, clear, backcolor.ToArgb(), IntPtr.Zero, true); + CheckAndThrow(); } public void StartRendering(bool clear, Color4 backcolor, Texture target, bool usedepthbuffer) { RenderDevice_StartRendering(Handle, clear, backcolor.ToArgb(), target.Handle, usedepthbuffer); + CheckAndThrow(); } public void FinishRendering() { RenderDevice_FinishRendering(Handle); + CheckAndThrow(); } public void Present() { RenderDevice_Present(Handle); + CheckAndThrow(); } public void ClearTexture(Color4 backcolor, Texture texture) { RenderDevice_ClearTexture(Handle, backcolor.ToArgb(), texture.Handle); + CheckAndThrow(); } public void CopyTexture(CubeTexture dst, CubeMapFace face) { RenderDevice_CopyTexture(Handle, dst.Handle, face); + CheckAndThrow(); } public void SetBufferData(IndexBuffer buffer, int[] data) { RenderDevice_SetIndexBufferData(Handle, buffer.Handle, data, data.Length * Marshal.SizeOf()); + CheckAndThrow(); } public void SetBufferData(VertexBuffer buffer, int length, VertexFormat format) { int stride = (format == VertexFormat.Flat) ? FlatVertex.Stride : WorldVertex.Stride; RenderDevice_SetVertexBufferData(Handle, buffer.Handle, IntPtr.Zero, length * stride, format); + CheckAndThrow(); } public void SetBufferData(VertexBuffer buffer, FlatVertex[] data) { RenderDevice_SetVertexBufferData(Handle, buffer.Handle, data, data.Length * Marshal.SizeOf(), VertexFormat.Flat); + CheckAndThrow(); } public void SetBufferData(VertexBuffer buffer, WorldVertex[] data) { RenderDevice_SetVertexBufferData(Handle, buffer.Handle, data, data.Length * Marshal.SizeOf(), VertexFormat.World); + CheckAndThrow(); } public void SetBufferSubdata(VertexBuffer buffer, long destOffset, FlatVertex[] data) { RenderDevice_SetVertexBufferSubdata(Handle, buffer.Handle, destOffset * FlatVertex.Stride, data, data.Length * FlatVertex.Stride); + CheckAndThrow(); } public void SetBufferSubdata(VertexBuffer buffer, long destOffset, WorldVertex[] data) { RenderDevice_SetVertexBufferSubdata(Handle, buffer.Handle, destOffset * WorldVertex.Stride, data, data.Length * WorldVertex.Stride); + CheckAndThrow(); } public void SetBufferSubdata(VertexBuffer buffer, FlatVertex[] data, long size) { if (size < 0 || size > data.Length) throw new ArgumentOutOfRangeException("size"); RenderDevice_SetVertexBufferSubdata(Handle, buffer.Handle, 0, data, size * FlatVertex.Stride); + CheckAndThrow(); } public void SetPixels(Texture texture, System.Drawing.Bitmap bitmap) @@ -290,6 +343,7 @@ namespace CodeImp.DoomBuilder.Rendering RenderDevice_SetPixels(Handle, texture.Handle, bmpdata.Scan0); bitmap.UnlockBits(bmpdata); + CheckAndThrow(); } public void SetPixels(CubeTexture texture, CubeMapFace face, System.Drawing.Bitmap bitmap) @@ -302,6 +356,7 @@ namespace CodeImp.DoomBuilder.Rendering RenderDevice_SetCubePixels(Handle, texture.Handle, face, bmpdata.Scan0); bitmap.UnlockBits(bmpdata); + CheckAndThrow(); } internal void RegisterResource(IRenderResource res) @@ -350,10 +405,13 @@ namespace CodeImp.DoomBuilder.Rendering static extern void RenderDevice_Delete(IntPtr handle); [DllImport("BuilderNative", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr RenderDevice_SetShader(IntPtr hwnd, ShaderName name); + static extern IntPtr RenderDevice_GetError(IntPtr handle); [DllImport("BuilderNative", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr RenderDevice_SetUniform(IntPtr hwnd, UniformName name, float[] data, int count); + static extern IntPtr RenderDevice_SetShader(IntPtr handle, ShaderName name); + + [DllImport("BuilderNative", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr RenderDevice_SetUniform(IntPtr handle, UniformName name, float[] data, int count); [DllImport("BuilderNative", CallingConvention = CallingConvention.Cdecl)] static extern void RenderDevice_SetVertexBuffer(IntPtr handle, IntPtr buffer); diff --git a/Source/Native/RenderDevice.cpp b/Source/Native/RenderDevice.cpp index cc95b2c6..93558931 100644 --- a/Source/Native/RenderDevice.cpp +++ b/Source/Native/RenderDevice.cpp @@ -1,805 +1,845 @@ - -#include "Precomp.h" -#include "RenderDevice.h" -#include "VertexBuffer.h" -#include "IndexBuffer.h" -#include "Texture.h" -#include "ShaderManager.h" -#include - -RenderDevice::RenderDevice(void* disp, void* window) -{ - memset(mUniforms, 0, sizeof(mUniforms)); - - Context = IOpenGLContext::Create(disp, window); - if (Context) - { - Context->MakeCurrent(); - - glGenVertexArrays(1, &mStreamVAO); - glGenBuffers(1, &mStreamVertexBuffer); - glBindVertexArray(mStreamVAO); - glBindBuffer(GL_ARRAY_BUFFER, mStreamVertexBuffer); - VertexBuffer::SetupFlatVAO(); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - mShaderManager = std::make_unique(); - - CheckError(); - Context->ClearCurrent(); - } -} - -RenderDevice::~RenderDevice() -{ - if (Context) - { - Context->MakeCurrent(); - glDeleteBuffers(1, &mStreamVertexBuffer); - glDeleteVertexArrays(1, &mStreamVAO); - mShaderManager->ReleaseResources(); - Context->ClearCurrent(); - } -} - -void RenderDevice::SetVertexBuffer(VertexBuffer* buffer) -{ - if (mVertexBuffer != buffer) - { - mVertexBuffer = buffer; - mNeedApply = true; - mVertexBufferChanged = true; - } -} - -void RenderDevice::SetIndexBuffer(IndexBuffer* buffer) -{ - if (mIndexBuffer != buffer) - { - mIndexBuffer = buffer; - mNeedApply = true; - mIndexBufferChanged = true; - } -} - -void RenderDevice::SetAlphaBlendEnable(bool value) -{ - if (mAlphaBlend != value) - { - mAlphaBlend = value; - mNeedApply = true; - mBlendStateChanged = true; - } -} - -void RenderDevice::SetAlphaTestEnable(bool value) -{ - if (mAlphaTest != value) - { - mAlphaTest = value; - mNeedApply = true; - mShaderChanged = true; - mUniformsChanged = true; - } -} - -void RenderDevice::SetCullMode(Cull mode) -{ - if (mCullMode != mode) - { - mCullMode = mode; - mNeedApply = true; - mRasterizerStateChanged = true; - } -} - -void RenderDevice::SetBlendOperation(BlendOperation op) -{ - if (mBlendOperation != op) - { - mBlendOperation = op; - mNeedApply = true; - mBlendStateChanged = true; - } -} - -void RenderDevice::SetSourceBlend(Blend blend) -{ - if (mSourceBlend != blend) - { - mSourceBlend = blend; - mNeedApply = true; - mBlendStateChanged = true; - } -} - -void RenderDevice::SetDestinationBlend(Blend blend) -{ - if (mDestinationBlend != blend) - { - mDestinationBlend = blend; - mNeedApply = true; - mBlendStateChanged = true; - } -} - -void RenderDevice::SetFillMode(FillMode mode) -{ - if (mFillMode != mode) - { - mFillMode = mode; - mNeedApply = true; - mRasterizerStateChanged = true; - } -} - -void RenderDevice::SetMultisampleAntialias(bool value) -{ -} - -void RenderDevice::SetZEnable(bool value) -{ - if (mDepthTest != value) - { - mDepthTest = value; - mNeedApply = true; - mDepthStateChanged = true; - } -} - -void RenderDevice::SetZWriteEnable(bool value) -{ - if (mDepthWrite != value) - { - mDepthWrite = value; - mNeedApply = true; - mDepthStateChanged = true; - } -} - -void RenderDevice::SetTexture(Texture* texture) -{ - if (mTextureUnit.Tex != texture) - { - mTextureUnit.Tex = texture; - mNeedApply = true; - mTexturesChanged = true; - } -} - -void RenderDevice::SetSamplerFilter(TextureFilter minfilter, TextureFilter magfilter, TextureFilter mipfilter, float maxanisotropy) -{ - auto glminfilter = GetGLMinFilter(minfilter, mipfilter); - auto glmagfilter = (magfilter == TextureFilter::Point || magfilter == TextureFilter::None) ? GL_NEAREST : GL_LINEAR; - if (mTextureUnit.MinFilter != glminfilter || mTextureUnit.MagFilter != glmagfilter || mTextureUnit.MaxAnisotropy != maxanisotropy) - { - mTextureUnit.MinFilter = glminfilter; - mTextureUnit.MagFilter = glmagfilter; - mTextureUnit.MaxAnisotropy = maxanisotropy; - mNeedApply = true; - mTexturesChanged = true; - } -} - -GLint RenderDevice::GetGLMinFilter(TextureFilter filter, TextureFilter mipfilter) -{ - if (mipfilter == TextureFilter::Linear) - { - if (filter == TextureFilter::Point || filter == TextureFilter::None) - return GL_LINEAR_MIPMAP_NEAREST; - else - return GL_LINEAR_MIPMAP_LINEAR; - } - else if (mipfilter == TextureFilter::Point) - { - if (filter == TextureFilter::Point || filter == TextureFilter::None) - return GL_NEAREST_MIPMAP_NEAREST; - else - return GL_NEAREST_MIPMAP_LINEAR; - } - else - { - if (filter == TextureFilter::Point || filter == TextureFilter::None) - return GL_NEAREST; - else - return GL_LINEAR; - } -} - -void RenderDevice::SetSamplerState(TextureAddress addressU, TextureAddress addressV, TextureAddress addressW) -{ - if (mTextureUnit.AddressU != addressU || mTextureUnit.AddressV != addressV || mTextureUnit.AddressW != addressW) - { - mTextureUnit.AddressU = addressU; - mTextureUnit.AddressV = addressV; - mTextureUnit.AddressW = addressW; - mNeedApply = true; - mTexturesChanged = true; - } -} - -void RenderDevice::ApplyViewport() -{ - glViewport(0, 0, mViewportWidth, mViewportHeight); -} - -void RenderDevice::Draw(PrimitiveType type, int startIndex, int primitiveCount) -{ - static const int modes[] = { GL_LINES, GL_TRIANGLES, GL_TRIANGLE_STRIP }; - static const int toVertexCount[] = { 2, 3, 1 }; - static const int toVertexStart[] = { 0, 0, 2 }; - - ApplyViewport(); - if (mNeedApply) ApplyChanges(); - glDrawArrays(modes[(int)type], startIndex, toVertexStart[(int)type] + primitiveCount * toVertexCount[(int)type]); -} - -void RenderDevice::DrawIndexed(PrimitiveType type, int startIndex, int primitiveCount) -{ - static const int modes[] = { GL_LINES, GL_TRIANGLES, GL_TRIANGLE_STRIP }; - static const int toVertexCount[] = { 2, 3, 1 }; - static const int toVertexStart[] = { 0, 0, 2 }; - - ApplyViewport(); - if (mNeedApply) ApplyChanges(); - glDrawElements(modes[(int)type], toVertexStart[(int)type] + primitiveCount * toVertexCount[(int)type], GL_UNSIGNED_INT, (const void*)(startIndex * sizeof(uint32_t))); -} - -void RenderDevice::DrawData(PrimitiveType type, int startIndex, int primitiveCount, const void* data) -{ - static const int modes[] = { GL_LINES, GL_TRIANGLES, GL_TRIANGLE_STRIP }; - static const int toVertexCount[] = { 2, 3, 1 }; - static const int toVertexStart[] = { 0, 0, 2 }; - - int vertcount = toVertexStart[(int)type] + primitiveCount * toVertexCount[(int)type]; - - ApplyViewport(); - if (mNeedApply) ApplyChanges(); - - glBindBuffer(GL_ARRAY_BUFFER, mStreamVertexBuffer); - glBufferData(GL_ARRAY_BUFFER, vertcount * (size_t)VertexBuffer::FlatStride, static_cast(data) + startIndex * (size_t)VertexBuffer::FlatStride, GL_STREAM_DRAW); - glBindVertexArray(mStreamVAO); - glDrawArrays(modes[(int)type], 0, vertcount); - ApplyVertexBuffer(); -} - -void RenderDevice::StartRendering(bool clear, int backcolor, Texture* target, bool usedepthbuffer) -{ - Context->MakeCurrent(); - mContextIsCurrent = true; - - if (target) - { - glBindFramebuffer(GL_FRAMEBUFFER, target->GetFramebuffer(usedepthbuffer)); - mViewportWidth = target->GetWidth(); - mViewportHeight = target->GetHeight(); - ApplyViewport(); - } - else - { - glBindFramebuffer(GL_FRAMEBUFFER, 0); - mViewportWidth = Context->GetWidth(); - mViewportHeight = Context->GetHeight(); - ApplyViewport(); - } - - if (clear && usedepthbuffer) - { - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); - glClearColor(RPART(backcolor) / 255.0f, GPART(backcolor) / 255.0f, BPART(backcolor) / 255.0f, APART(backcolor) / 255.0f); - glClearDepthf(1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - } - else if (clear) - { - glClearColor(RPART(backcolor) / 255.0f, GPART(backcolor) / 255.0f, BPART(backcolor) / 255.0f, APART(backcolor) / 255.0f); - glClear(GL_COLOR_BUFFER_BIT); - } - - mNeedApply = true; - mShaderChanged = true; - mUniformsChanged = true; - mTexturesChanged = true; - mIndexBufferChanged = true; - mVertexBufferChanged = true; - mDepthStateChanged = true; - mBlendStateChanged = true; - mRasterizerStateChanged = true; -} - -void RenderDevice::FinishRendering() -{ - CheckError(); - Context->ClearCurrent(); - mContextIsCurrent = false; -} - -void RenderDevice::Present() -{ - Context->SwapBuffers(); -} - -void RenderDevice::ClearTexture(int backcolor, Texture* texture) -{ - StartRendering(true, backcolor, texture, false); - FinishRendering(); -} - -void RenderDevice::CopyTexture(Texture* dst, CubeMapFace face) -{ - static const GLenum facegl[] = { - GL_TEXTURE_CUBE_MAP_POSITIVE_X, - GL_TEXTURE_CUBE_MAP_POSITIVE_Y, - GL_TEXTURE_CUBE_MAP_POSITIVE_Z, - GL_TEXTURE_CUBE_MAP_NEGATIVE_X, - GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, - GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - }; - - if (!mContextIsCurrent) Context->MakeCurrent(); - GLint oldTexture = 0; - glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &oldTexture); - - glBindTexture(GL_TEXTURE_CUBE_MAP, dst->GetTexture()); - glCopyTexSubImage2D(facegl[(int)face], 0, 0, 0, 0, 0, dst->GetWidth(), dst->GetHeight()); - if (face == CubeMapFace::NegativeZ) - glGenerateMipmap(GL_TEXTURE_CUBE_MAP); - - glBindTexture(GL_TEXTURE_CUBE_MAP, oldTexture); - if (!mContextIsCurrent) Context->ClearCurrent(); -} - -void RenderDevice::SetVertexBufferData(VertexBuffer* buffer, void* data, int64_t size, VertexFormat format) -{ - if (!mContextIsCurrent) Context->MakeCurrent(); - buffer->Format = format; - GLint oldbinding = 0; - glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &oldbinding); - glBindBuffer(GL_ARRAY_BUFFER, buffer->GetBuffer()); - glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, oldbinding); - if (!mContextIsCurrent) Context->ClearCurrent(); -} - -void RenderDevice::SetVertexBufferSubdata(VertexBuffer* buffer, int64_t destOffset, void* data, int64_t size) -{ - if (!mContextIsCurrent) Context->MakeCurrent(); - GLint oldbinding = 0; - glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &oldbinding); - glBindBuffer(GL_ARRAY_BUFFER, buffer->GetBuffer()); - glBufferSubData(GL_ARRAY_BUFFER, destOffset, size, data); - glBindBuffer(GL_ARRAY_BUFFER, oldbinding); - if (!mContextIsCurrent) Context->ClearCurrent(); -} - -void RenderDevice::SetIndexBufferData(IndexBuffer* buffer, void* data, int64_t size) -{ - if (!mContextIsCurrent) Context->MakeCurrent(); - GLint oldbinding = 0; - glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &oldbinding); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->GetBuffer()); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, GL_STATIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oldbinding); - if (!mContextIsCurrent) Context->ClearCurrent(); -} - -void RenderDevice::SetPixels(Texture* texture, const void* data) -{ - texture->SetPixels(data); - InvalidateTexture(texture); -} - -void RenderDevice::SetCubePixels(Texture* texture, CubeMapFace face, const void* data) -{ - texture->SetCubePixels(face, data); - InvalidateTexture(texture); -} - -void RenderDevice::InvalidateTexture(Texture* texture) -{ - if (texture->IsTextureCreated()) - { - if (!mContextIsCurrent) Context->MakeCurrent(); - texture->Invalidate(); - if (!mContextIsCurrent) Context->ClearCurrent(); - mNeedApply = true; - mTexturesChanged = true; - } -} - -void RenderDevice::CheckError() -{ - GLenum error = glGetError(); - if (error != GL_NO_ERROR) - throw std::runtime_error("OpenGL error!"); -} - -Shader* RenderDevice::GetActiveShader() -{ - if (mAlphaTest) - return &mShaderManager->AlphaTestShaders[(int)mShaderName]; - else - return &mShaderManager->Shaders[(int)mShaderName]; -} - -void RenderDevice::SetShader(ShaderName name) -{ - if (name != mShaderName) - { - mShaderName = name; - mNeedApply = true; - mShaderChanged = true; - mUniformsChanged = true; - } -} - -static const int uniformLocations[(int)UniformName::NumUniforms] = { - 64, // rendersettings - 0, // projection - 108, // desaturation - 80, // highlightcolor - 16, // view - 32, // world - 48, // modelnormal - 68, // FillColor - 72, // vertexColor - 84, // stencilColor - 92, // lightPosAndRadius - 96, // lightOrientation - 100, // light2Radius - 104, // lightColor - 109, // ignoreNormals - 110, // spotLight - 76, // campos, - 112, // texturefactor - 116, // fogsettings - 120, // fogcolor -}; - -void RenderDevice::SetUniform(UniformName name, const void* values, int count) -{ - auto dest = &mUniforms[uniformLocations[(int)name]]; - if (memcmp(dest, values, sizeof(float) * count) != 0) - { - memcpy(dest, values, sizeof(float) * count); - mNeedApply = true; - mUniformsChanged = true; - } -} - -void RenderDevice::ApplyChanges() -{ - if (mShaderChanged) - ApplyShader(); - if (mVertexBufferChanged) - ApplyVertexBuffer(); - if (mIndexBufferChanged) - ApplyIndexBuffer(); - if (mUniformsChanged) - ApplyUniforms(); - if (mTexturesChanged) - ApplyTextures(); - if (mRasterizerStateChanged) - ApplyRasterizerState(); - if (mBlendStateChanged) - ApplyBlendState(); - if (mDepthStateChanged) - ApplyDepthState(); - - mNeedApply = false; -} - -void RenderDevice::ApplyShader() -{ - GetActiveShader()->Bind(); - mShaderChanged = false; -} - -void RenderDevice::ApplyRasterizerState() -{ - if (mCullMode == Cull::None) - { - glDisable(GL_CULL_FACE); - } - else - { - glEnable(GL_CULL_FACE); - glFrontFace(GL_CW); - } - - GLenum fillMode2GL[] = { GL_FILL, GL_LINE }; - glPolygonMode(GL_FRONT_AND_BACK, fillMode2GL[(int)mFillMode]); - - mRasterizerStateChanged = false; -} - -void RenderDevice::ApplyBlendState() -{ - if (mAlphaBlend) - { - static const GLenum blendOp2GL[] = { GL_FUNC_ADD, GL_FUNC_REVERSE_SUBTRACT }; - static const GLenum blendFunc2GL[] = { GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA, GL_ONE }; - - glEnable(GL_BLEND); - glBlendEquation(blendOp2GL[(int)mBlendOperation]); - glBlendFunc(blendFunc2GL[(int)mSourceBlend], blendFunc2GL[(int)mDestinationBlend]); - } - else - { - glDisable(GL_BLEND); - } - - mBlendStateChanged = false; -} - -void RenderDevice::ApplyDepthState() -{ - if (mDepthTest) - { - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - glDepthMask(mDepthWrite ? GL_TRUE : GL_FALSE); - } - else - { - glDisable(GL_DEPTH_TEST); - } - - mDepthStateChanged = false; -} - -void RenderDevice::ApplyIndexBuffer() -{ - if (mIndexBuffer) - { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->GetBuffer()); - } - else - { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } - - mIndexBufferChanged = false; -} - -void RenderDevice::ApplyVertexBuffer() -{ - if (mVertexBuffer) - glBindVertexArray(mVertexBuffer->GetVAO()); - - mVertexBufferChanged = false; -} - -void RenderDevice::ApplyUniforms() -{ - Shader* shader = GetActiveShader(); - auto& locations = shader->UniformLocations; - - glUniformMatrix4fv(locations[(int)UniformName::projection], 1, GL_FALSE, &mUniforms[0].valuef); - glUniformMatrix4fv(locations[(int)UniformName::view], 1, GL_FALSE, &mUniforms[16].valuef); - glUniformMatrix4fv(locations[(int)UniformName::world], 1, GL_FALSE, &mUniforms[32].valuef); - glUniformMatrix4fv(locations[(int)UniformName::modelnormal], 1, GL_FALSE, &mUniforms[48].valuef); - - glUniform4fv(locations[(int)UniformName::rendersettings], 1, &mUniforms[64].valuef); - glUniform4fv(locations[(int)UniformName::FillColor], 1, &mUniforms[68].valuef); - glUniform4fv(locations[(int)UniformName::vertexColor], 1, &mUniforms[72].valuef); - glUniform4fv(locations[(int)UniformName::campos], 1, &mUniforms[76].valuef); - glUniform4fv(locations[(int)UniformName::highlightcolor], 1, &mUniforms[80].valuef); - glUniform4fv(locations[(int)UniformName::stencilColor], 1, &mUniforms[84].valuef); - glUniform4fv(locations[(int)UniformName::lightColor], 1, &mUniforms[88].valuef); - glUniform4fv(locations[(int)UniformName::lightPosAndRadius], 1, &mUniforms[92].valuef); - glUniform3fv(locations[(int)UniformName::lightOrientation], 1, &mUniforms[96].valuef); - glUniform2fv(locations[(int)UniformName::light2Radius], 1, &mUniforms[100].valuef); - glUniform4fv(locations[(int)UniformName::lightColor], 1, &mUniforms[104].valuef); - - glUniform1fv(locations[(int)UniformName::desaturation], 1, &mUniforms[108].valuef); - glUniform1fv(locations[(int)UniformName::ignoreNormals], 1, &mUniforms[109].valuef); - glUniform1fv(locations[(int)UniformName::spotLight], 1, &mUniforms[110].valuef); - - glUniform4fv(locations[(int)UniformName::texturefactor], 1, &mUniforms[112].valuef); - glUniform4fv(locations[(int)UniformName::fogsettings], 1, &mUniforms[116].valuef); - glUniform4fv(locations[(int)UniformName::fogcolor], 1, &mUniforms[120].valuef); - - mUniformsChanged = false; -} - -void RenderDevice::ApplyTextures() -{ - static const int wrapMode[] = { GL_REPEAT, GL_CLAMP_TO_EDGE }; - - glActiveTexture(GL_TEXTURE0); - if (mTextureUnit.Tex) - { - GLenum target = mTextureUnit.Tex->IsCubeTexture() ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; - - glBindTexture(target, mTextureUnit.Tex->GetTexture()); - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, mTextureUnit.MinFilter); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, mTextureUnit.MagFilter); - glTexParameteri(target, GL_TEXTURE_WRAP_S, wrapMode[(int)mTextureUnit.AddressU]); - glTexParameteri(target, GL_TEXTURE_WRAP_T, wrapMode[(int)mTextureUnit.AddressV]); - glTexParameteri(target, GL_TEXTURE_WRAP_R, wrapMode[(int)mTextureUnit.AddressW]); - } - else - { - glBindTexture(GL_TEXTURE_2D, 0); - } - - mTexturesChanged = false; -} - -///////////////////////////////////////////////////////////////////////////// - -extern "C" -{ - -RenderDevice* RenderDevice_New(void* disp, void* window) -{ - RenderDevice *device = new RenderDevice(disp, window); - if (!device->Context) - { - delete device; - return nullptr; - } - else - { - return device; - } -} - -void RenderDevice_Delete(RenderDevice* device) -{ - delete device; -} - -void RenderDevice_SetShader(RenderDevice* device, ShaderName name) -{ - device->SetShader(name); -} - -void RenderDevice_SetUniform(RenderDevice* device, UniformName name, const void* values, int count) -{ - device->SetUniform(name, values, count); -} - -void RenderDevice_SetVertexBuffer(RenderDevice* device, VertexBuffer* buffer) -{ - device->SetVertexBuffer(buffer); -} - -void RenderDevice_SetIndexBuffer(RenderDevice* device, IndexBuffer* buffer) -{ - device->SetIndexBuffer(buffer); -} - -void RenderDevice_SetAlphaBlendEnable(RenderDevice* device, bool value) -{ - device->SetAlphaBlendEnable(value); -} - -void RenderDevice_SetAlphaTestEnable(RenderDevice* device, bool value) -{ - device->SetAlphaTestEnable(value); -} - -void RenderDevice_SetCullMode(RenderDevice* device, Cull mode) -{ - device->SetCullMode(mode); -} - -void RenderDevice_SetBlendOperation(RenderDevice* device, BlendOperation op) -{ - device->SetBlendOperation(op); -} - -void RenderDevice_SetSourceBlend(RenderDevice* device, Blend blend) -{ - device->SetSourceBlend(blend); -} - -void RenderDevice_SetDestinationBlend(RenderDevice* device, Blend blend) -{ - device->SetDestinationBlend(blend); -} - -void RenderDevice_SetFillMode(RenderDevice* device, FillMode mode) -{ - device->SetFillMode(mode); -} - -void RenderDevice_SetMultisampleAntialias(RenderDevice* device, bool value) -{ - device->SetMultisampleAntialias(value); -} - -void RenderDevice_SetZEnable(RenderDevice* device, bool value) -{ - device->SetZEnable(value); -} - -void RenderDevice_SetZWriteEnable(RenderDevice* device, bool value) -{ - device->SetZWriteEnable(value); -} - -void RenderDevice_SetTexture(RenderDevice* device, Texture* texture) -{ - device->SetTexture(texture); -} - -void RenderDevice_SetSamplerFilter(RenderDevice* device, TextureFilter minfilter, TextureFilter magfilter, TextureFilter mipfilter, float maxanisotropy) -{ - device->SetSamplerFilter(minfilter, magfilter, mipfilter, maxanisotropy); -} - -void RenderDevice_SetSamplerState(RenderDevice* device, TextureAddress addressU, TextureAddress addressV, TextureAddress addressW) -{ - device->SetSamplerState(addressU, addressV, addressW); -} - -void RenderDevice_Draw(RenderDevice* device, PrimitiveType type, int startIndex, int primitiveCount) -{ - device->Draw(type, startIndex, primitiveCount); -} - -void RenderDevice_DrawIndexed(RenderDevice* device, PrimitiveType type, int startIndex, int primitiveCount) -{ - device->DrawIndexed(type, startIndex, primitiveCount); -} - -void RenderDevice_DrawData(RenderDevice* device, PrimitiveType type, int startIndex, int primitiveCount, const void* data) -{ - device->DrawData(type, startIndex, primitiveCount, data); -} - -void RenderDevice_StartRendering(RenderDevice* device, bool clear, int backcolor, Texture* target, bool usedepthbuffer) -{ - device->StartRendering(clear, backcolor, target, usedepthbuffer); -} - -void RenderDevice_FinishRendering(RenderDevice* device) -{ - device->FinishRendering(); -} - -void RenderDevice_Present(RenderDevice* device) -{ - device->Present(); -} - -void RenderDevice_ClearTexture(RenderDevice* device, int backcolor, Texture* texture) -{ - device->ClearTexture(backcolor, texture); -} - -void RenderDevice_CopyTexture(RenderDevice* device, Texture* dst, CubeMapFace face) -{ - device->CopyTexture(dst, face); -} - -void RenderDevice_SetVertexBufferData(RenderDevice* device, VertexBuffer* buffer, void* data, int64_t size, VertexFormat format) -{ - device->SetVertexBufferData(buffer, data, size, format); -} - -void RenderDevice_SetVertexBufferSubdata(RenderDevice* device, VertexBuffer* buffer, int64_t destOffset, void* data, int64_t size) -{ - device->SetVertexBufferSubdata(buffer, destOffset, data, size); -} - -void RenderDevice_SetIndexBufferData(RenderDevice* device, IndexBuffer* buffer, void* data, int64_t size) -{ - device->SetIndexBufferData(buffer, data, size); -} - -void RenderDevice_SetPixels(RenderDevice* device, Texture* texture, const void* data) -{ - device->SetPixels(texture, data); -} - -void RenderDevice_SetCubePixels(RenderDevice* device, Texture* texture, CubeMapFace face, const void* data) -{ - device->SetCubePixels(texture, face, data); -} - -} + +#include "Precomp.h" +#include "RenderDevice.h" +#include "VertexBuffer.h" +#include "IndexBuffer.h" +#include "Texture.h" +#include "ShaderManager.h" +#include +#include + +RenderDevice::RenderDevice(void* disp, void* window) +{ + memset(mUniforms, 0, sizeof(mUniforms)); + memset(mLastError, 0, sizeof(mLastError)); + memset(mReturnError, 0, sizeof(mReturnError)); + + Context = IOpenGLContext::Create(disp, window); + if (Context) + { + Context->MakeCurrent(); + + glGenVertexArrays(1, &mStreamVAO); + glGenBuffers(1, &mStreamVertexBuffer); + glBindVertexArray(mStreamVAO); + glBindBuffer(GL_ARRAY_BUFFER, mStreamVertexBuffer); + VertexBuffer::SetupFlatVAO(); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + mShaderManager = std::make_unique(); + + CheckGLError(); + Context->ClearCurrent(); + } +} + +RenderDevice::~RenderDevice() +{ + if (Context) + { + Context->MakeCurrent(); + glDeleteBuffers(1, &mStreamVertexBuffer); + glDeleteVertexArrays(1, &mStreamVAO); + mShaderManager->ReleaseResources(); + Context->ClearCurrent(); + } +} + +void RenderDevice::SetVertexBuffer(VertexBuffer* buffer) +{ + if (mVertexBuffer != buffer) + { + mVertexBuffer = buffer; + mNeedApply = true; + mVertexBufferChanged = true; + } +} + +void RenderDevice::SetIndexBuffer(IndexBuffer* buffer) +{ + if (mIndexBuffer != buffer) + { + mIndexBuffer = buffer; + mNeedApply = true; + mIndexBufferChanged = true; + } +} + +void RenderDevice::SetAlphaBlendEnable(bool value) +{ + if (mAlphaBlend != value) + { + mAlphaBlend = value; + mNeedApply = true; + mBlendStateChanged = true; + } +} + +void RenderDevice::SetAlphaTestEnable(bool value) +{ + if (mAlphaTest != value) + { + mAlphaTest = value; + mNeedApply = true; + mShaderChanged = true; + mUniformsChanged = true; + } +} + +void RenderDevice::SetCullMode(Cull mode) +{ + if (mCullMode != mode) + { + mCullMode = mode; + mNeedApply = true; + mRasterizerStateChanged = true; + } +} + +void RenderDevice::SetBlendOperation(BlendOperation op) +{ + if (mBlendOperation != op) + { + mBlendOperation = op; + mNeedApply = true; + mBlendStateChanged = true; + } +} + +void RenderDevice::SetSourceBlend(Blend blend) +{ + if (mSourceBlend != blend) + { + mSourceBlend = blend; + mNeedApply = true; + mBlendStateChanged = true; + } +} + +void RenderDevice::SetDestinationBlend(Blend blend) +{ + if (mDestinationBlend != blend) + { + mDestinationBlend = blend; + mNeedApply = true; + mBlendStateChanged = true; + } +} + +void RenderDevice::SetFillMode(FillMode mode) +{ + if (mFillMode != mode) + { + mFillMode = mode; + mNeedApply = true; + mRasterizerStateChanged = true; + } +} + +void RenderDevice::SetMultisampleAntialias(bool value) +{ +} + +void RenderDevice::SetZEnable(bool value) +{ + if (mDepthTest != value) + { + mDepthTest = value; + mNeedApply = true; + mDepthStateChanged = true; + } +} + +void RenderDevice::SetZWriteEnable(bool value) +{ + if (mDepthWrite != value) + { + mDepthWrite = value; + mNeedApply = true; + mDepthStateChanged = true; + } +} + +void RenderDevice::SetTexture(Texture* texture) +{ + if (mTextureUnit.Tex != texture) + { + mTextureUnit.Tex = texture; + mNeedApply = true; + mTexturesChanged = true; + } +} + +void RenderDevice::SetSamplerFilter(TextureFilter minfilter, TextureFilter magfilter, TextureFilter mipfilter, float maxanisotropy) +{ + auto glminfilter = GetGLMinFilter(minfilter, mipfilter); + auto glmagfilter = (magfilter == TextureFilter::Point || magfilter == TextureFilter::None) ? GL_NEAREST : GL_LINEAR; + if (mTextureUnit.MinFilter != glminfilter || mTextureUnit.MagFilter != glmagfilter || mTextureUnit.MaxAnisotropy != maxanisotropy) + { + mTextureUnit.MinFilter = glminfilter; + mTextureUnit.MagFilter = glmagfilter; + mTextureUnit.MaxAnisotropy = maxanisotropy; + mNeedApply = true; + mTexturesChanged = true; + } +} + +GLint RenderDevice::GetGLMinFilter(TextureFilter filter, TextureFilter mipfilter) +{ + if (mipfilter == TextureFilter::Linear) + { + if (filter == TextureFilter::Point || filter == TextureFilter::None) + return GL_LINEAR_MIPMAP_NEAREST; + else + return GL_LINEAR_MIPMAP_LINEAR; + } + else if (mipfilter == TextureFilter::Point) + { + if (filter == TextureFilter::Point || filter == TextureFilter::None) + return GL_NEAREST_MIPMAP_NEAREST; + else + return GL_NEAREST_MIPMAP_LINEAR; + } + else + { + if (filter == TextureFilter::Point || filter == TextureFilter::None) + return GL_NEAREST; + else + return GL_LINEAR; + } +} + +void RenderDevice::SetSamplerState(TextureAddress addressU, TextureAddress addressV, TextureAddress addressW) +{ + if (mTextureUnit.AddressU != addressU || mTextureUnit.AddressV != addressV || mTextureUnit.AddressW != addressW) + { + mTextureUnit.AddressU = addressU; + mTextureUnit.AddressV = addressV; + mTextureUnit.AddressW = addressW; + mNeedApply = true; + mTexturesChanged = true; + } +} + +void RenderDevice::ApplyViewport() +{ + glViewport(0, 0, mViewportWidth, mViewportHeight); +} + +void RenderDevice::Draw(PrimitiveType type, int startIndex, int primitiveCount) +{ + static const int modes[] = { GL_LINES, GL_TRIANGLES, GL_TRIANGLE_STRIP }; + static const int toVertexCount[] = { 2, 3, 1 }; + static const int toVertexStart[] = { 0, 0, 2 }; + + ApplyViewport(); + if (mNeedApply) ApplyChanges(); + glDrawArrays(modes[(int)type], startIndex, toVertexStart[(int)type] + primitiveCount * toVertexCount[(int)type]); +} + +void RenderDevice::DrawIndexed(PrimitiveType type, int startIndex, int primitiveCount) +{ + static const int modes[] = { GL_LINES, GL_TRIANGLES, GL_TRIANGLE_STRIP }; + static const int toVertexCount[] = { 2, 3, 1 }; + static const int toVertexStart[] = { 0, 0, 2 }; + + ApplyViewport(); + if (mNeedApply) ApplyChanges(); + glDrawElements(modes[(int)type], toVertexStart[(int)type] + primitiveCount * toVertexCount[(int)type], GL_UNSIGNED_INT, (const void*)(startIndex * sizeof(uint32_t))); +} + +void RenderDevice::DrawData(PrimitiveType type, int startIndex, int primitiveCount, const void* data) +{ + static const int modes[] = { GL_LINES, GL_TRIANGLES, GL_TRIANGLE_STRIP }; + static const int toVertexCount[] = { 2, 3, 1 }; + static const int toVertexStart[] = { 0, 0, 2 }; + + int vertcount = toVertexStart[(int)type] + primitiveCount * toVertexCount[(int)type]; + + ApplyViewport(); + if (mNeedApply) ApplyChanges(); + + glBindBuffer(GL_ARRAY_BUFFER, mStreamVertexBuffer); + glBufferData(GL_ARRAY_BUFFER, vertcount * (size_t)VertexBuffer::FlatStride, static_cast(data) + startIndex * (size_t)VertexBuffer::FlatStride, GL_STREAM_DRAW); + glBindVertexArray(mStreamVAO); + glDrawArrays(modes[(int)type], 0, vertcount); + ApplyVertexBuffer(); +} + +void RenderDevice::StartRendering(bool clear, int backcolor, Texture* target, bool usedepthbuffer) +{ + Context->MakeCurrent(); + mContextIsCurrent = true; + + if (target) + { + GLuint framebuffer = 0; + try + { + framebuffer = target->GetFramebuffer(usedepthbuffer); + } + catch (std::runtime_error& e) + { + SetError("Error setting render target: %s", e.what()); + } + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + mViewportWidth = target->GetWidth(); + mViewportHeight = target->GetHeight(); + ApplyViewport(); + } + else + { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + mViewportWidth = Context->GetWidth(); + mViewportHeight = Context->GetHeight(); + ApplyViewport(); + } + + if (clear && usedepthbuffer) + { + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glClearColor(RPART(backcolor) / 255.0f, GPART(backcolor) / 255.0f, BPART(backcolor) / 255.0f, APART(backcolor) / 255.0f); + glClearDepthf(1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + else if (clear) + { + glClearColor(RPART(backcolor) / 255.0f, GPART(backcolor) / 255.0f, BPART(backcolor) / 255.0f, APART(backcolor) / 255.0f); + glClear(GL_COLOR_BUFFER_BIT); + } + + mNeedApply = true; + mShaderChanged = true; + mUniformsChanged = true; + mTexturesChanged = true; + mIndexBufferChanged = true; + mVertexBufferChanged = true; + mDepthStateChanged = true; + mBlendStateChanged = true; + mRasterizerStateChanged = true; +} + +void RenderDevice::FinishRendering() +{ + CheckGLError(); + Context->ClearCurrent(); + mContextIsCurrent = false; +} + +void RenderDevice::Present() +{ + Context->SwapBuffers(); +} + +void RenderDevice::ClearTexture(int backcolor, Texture* texture) +{ + StartRendering(true, backcolor, texture, false); + FinishRendering(); +} + +void RenderDevice::CopyTexture(Texture* dst, CubeMapFace face) +{ + static const GLenum facegl[] = { + GL_TEXTURE_CUBE_MAP_POSITIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z + }; + + if (!mContextIsCurrent) Context->MakeCurrent(); + GLint oldTexture = 0; + glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &oldTexture); + + glBindTexture(GL_TEXTURE_CUBE_MAP, dst->GetTexture()); + glCopyTexSubImage2D(facegl[(int)face], 0, 0, 0, 0, 0, dst->GetWidth(), dst->GetHeight()); + if (face == CubeMapFace::NegativeZ) + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); + + glBindTexture(GL_TEXTURE_CUBE_MAP, oldTexture); + if (!mContextIsCurrent) Context->ClearCurrent(); +} + +void RenderDevice::SetVertexBufferData(VertexBuffer* buffer, void* data, int64_t size, VertexFormat format) +{ + if (!mContextIsCurrent) Context->MakeCurrent(); + buffer->Format = format; + GLint oldbinding = 0; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &oldbinding); + glBindBuffer(GL_ARRAY_BUFFER, buffer->GetBuffer()); + glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, oldbinding); + if (!mContextIsCurrent) Context->ClearCurrent(); +} + +void RenderDevice::SetVertexBufferSubdata(VertexBuffer* buffer, int64_t destOffset, void* data, int64_t size) +{ + if (!mContextIsCurrent) Context->MakeCurrent(); + GLint oldbinding = 0; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &oldbinding); + glBindBuffer(GL_ARRAY_BUFFER, buffer->GetBuffer()); + glBufferSubData(GL_ARRAY_BUFFER, destOffset, size, data); + glBindBuffer(GL_ARRAY_BUFFER, oldbinding); + if (!mContextIsCurrent) Context->ClearCurrent(); +} + +void RenderDevice::SetIndexBufferData(IndexBuffer* buffer, void* data, int64_t size) +{ + if (!mContextIsCurrent) Context->MakeCurrent(); + GLint oldbinding = 0; + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &oldbinding); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->GetBuffer()); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oldbinding); + if (!mContextIsCurrent) Context->ClearCurrent(); +} + +void RenderDevice::SetPixels(Texture* texture, const void* data) +{ + texture->SetPixels(data); + InvalidateTexture(texture); +} + +void RenderDevice::SetCubePixels(Texture* texture, CubeMapFace face, const void* data) +{ + texture->SetCubePixels(face, data); + InvalidateTexture(texture); +} + +void RenderDevice::InvalidateTexture(Texture* texture) +{ + if (texture->IsTextureCreated()) + { + if (!mContextIsCurrent) Context->MakeCurrent(); + texture->Invalidate(); + if (!mContextIsCurrent) Context->ClearCurrent(); + mNeedApply = true; + mTexturesChanged = true; + } +} + +void RenderDevice::CheckGLError() +{ + GLenum error = glGetError(); + if (error != GL_NO_ERROR) + SetError("OpenGL error: %d", error); +} + +void RenderDevice::SetError(const char* fmt, ...) +{ + va_list va; + va_start(va, fmt); + mLastError[sizeof(mLastError) - 1] = 0; + _vsnprintf(mLastError, sizeof(mLastError)-1, fmt, va); + va_end(va); +} + +const char* RenderDevice::GetError() +{ + memcpy(mReturnError, mLastError, sizeof(mReturnError)); + mLastError[0] = 0; + return mReturnError; +} + +Shader* RenderDevice::GetActiveShader() +{ + if (mAlphaTest) + return &mShaderManager->AlphaTestShaders[(int)mShaderName]; + else + return &mShaderManager->Shaders[(int)mShaderName]; +} + +void RenderDevice::SetShader(ShaderName name) +{ + if (name != mShaderName) + { + mShaderName = name; + mNeedApply = true; + mShaderChanged = true; + mUniformsChanged = true; + } +} + +static const int uniformLocations[(int)UniformName::NumUniforms] = { + 64, // rendersettings + 0, // projection + 108, // desaturation + 80, // highlightcolor + 16, // view + 32, // world + 48, // modelnormal + 68, // FillColor + 72, // vertexColor + 84, // stencilColor + 92, // lightPosAndRadius + 96, // lightOrientation + 100, // light2Radius + 104, // lightColor + 109, // ignoreNormals + 110, // spotLight + 76, // campos, + 112, // texturefactor + 116, // fogsettings + 120, // fogcolor +}; + +void RenderDevice::SetUniform(UniformName name, const void* values, int count) +{ + auto dest = &mUniforms[uniformLocations[(int)name]]; + if (memcmp(dest, values, sizeof(float) * count) != 0) + { + memcpy(dest, values, sizeof(float) * count); + mNeedApply = true; + mUniformsChanged = true; + } +} + +void RenderDevice::ApplyChanges() +{ + if (mShaderChanged) + ApplyShader(); + if (mVertexBufferChanged) + ApplyVertexBuffer(); + if (mIndexBufferChanged) + ApplyIndexBuffer(); + if (mUniformsChanged) + ApplyUniforms(); + if (mTexturesChanged) + ApplyTextures(); + if (mRasterizerStateChanged) + ApplyRasterizerState(); + if (mBlendStateChanged) + ApplyBlendState(); + if (mDepthStateChanged) + ApplyDepthState(); + + mNeedApply = false; +} + +void RenderDevice::ApplyShader() +{ + Shader* curShader = GetActiveShader(); + if (!curShader->CheckCompile()) + { + SetError("Failed to bind shader:\r\n%s", curShader->GetCompileError().c_str()); + return; + } + + curShader->Bind(); + mShaderChanged = false; +} + +void RenderDevice::ApplyRasterizerState() +{ + if (mCullMode == Cull::None) + { + glDisable(GL_CULL_FACE); + } + else + { + glEnable(GL_CULL_FACE); + glFrontFace(GL_CW); + } + + GLenum fillMode2GL[] = { GL_FILL, GL_LINE }; + glPolygonMode(GL_FRONT_AND_BACK, fillMode2GL[(int)mFillMode]); + + mRasterizerStateChanged = false; +} + +void RenderDevice::ApplyBlendState() +{ + if (mAlphaBlend) + { + static const GLenum blendOp2GL[] = { GL_FUNC_ADD, GL_FUNC_REVERSE_SUBTRACT }; + static const GLenum blendFunc2GL[] = { GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA, GL_ONE }; + + glEnable(GL_BLEND); + glBlendEquation(blendOp2GL[(int)mBlendOperation]); + glBlendFunc(blendFunc2GL[(int)mSourceBlend], blendFunc2GL[(int)mDestinationBlend]); + } + else + { + glDisable(GL_BLEND); + } + + mBlendStateChanged = false; +} + +void RenderDevice::ApplyDepthState() +{ + if (mDepthTest) + { + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glDepthMask(mDepthWrite ? GL_TRUE : GL_FALSE); + } + else + { + glDisable(GL_DEPTH_TEST); + } + + mDepthStateChanged = false; +} + +void RenderDevice::ApplyIndexBuffer() +{ + if (mIndexBuffer) + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->GetBuffer()); + } + else + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + mIndexBufferChanged = false; +} + +void RenderDevice::ApplyVertexBuffer() +{ + if (mVertexBuffer) + glBindVertexArray(mVertexBuffer->GetVAO()); + + mVertexBufferChanged = false; +} + +void RenderDevice::ApplyUniforms() +{ + Shader* shader = GetActiveShader(); + auto& locations = shader->UniformLocations; + + glUniformMatrix4fv(locations[(int)UniformName::projection], 1, GL_FALSE, &mUniforms[0].valuef); + glUniformMatrix4fv(locations[(int)UniformName::view], 1, GL_FALSE, &mUniforms[16].valuef); + glUniformMatrix4fv(locations[(int)UniformName::world], 1, GL_FALSE, &mUniforms[32].valuef); + glUniformMatrix4fv(locations[(int)UniformName::modelnormal], 1, GL_FALSE, &mUniforms[48].valuef); + + glUniform4fv(locations[(int)UniformName::rendersettings], 1, &mUniforms[64].valuef); + glUniform4fv(locations[(int)UniformName::FillColor], 1, &mUniforms[68].valuef); + glUniform4fv(locations[(int)UniformName::vertexColor], 1, &mUniforms[72].valuef); + glUniform4fv(locations[(int)UniformName::campos], 1, &mUniforms[76].valuef); + glUniform4fv(locations[(int)UniformName::highlightcolor], 1, &mUniforms[80].valuef); + glUniform4fv(locations[(int)UniformName::stencilColor], 1, &mUniforms[84].valuef); + glUniform4fv(locations[(int)UniformName::lightColor], 1, &mUniforms[88].valuef); + glUniform4fv(locations[(int)UniformName::lightPosAndRadius], 1, &mUniforms[92].valuef); + glUniform3fv(locations[(int)UniformName::lightOrientation], 1, &mUniforms[96].valuef); + glUniform2fv(locations[(int)UniformName::light2Radius], 1, &mUniforms[100].valuef); + glUniform4fv(locations[(int)UniformName::lightColor], 1, &mUniforms[104].valuef); + + glUniform1fv(locations[(int)UniformName::desaturation], 1, &mUniforms[108].valuef); + glUniform1fv(locations[(int)UniformName::ignoreNormals], 1, &mUniforms[109].valuef); + glUniform1fv(locations[(int)UniformName::spotLight], 1, &mUniforms[110].valuef); + + glUniform4fv(locations[(int)UniformName::texturefactor], 1, &mUniforms[112].valuef); + glUniform4fv(locations[(int)UniformName::fogsettings], 1, &mUniforms[116].valuef); + glUniform4fv(locations[(int)UniformName::fogcolor], 1, &mUniforms[120].valuef); + + mUniformsChanged = false; +} + +void RenderDevice::ApplyTextures() +{ + static const int wrapMode[] = { GL_REPEAT, GL_CLAMP_TO_EDGE }; + + glActiveTexture(GL_TEXTURE0); + if (mTextureUnit.Tex) + { + GLenum target = mTextureUnit.Tex->IsCubeTexture() ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; + + glBindTexture(target, mTextureUnit.Tex->GetTexture()); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, mTextureUnit.MinFilter); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, mTextureUnit.MagFilter); + glTexParameteri(target, GL_TEXTURE_WRAP_S, wrapMode[(int)mTextureUnit.AddressU]); + glTexParameteri(target, GL_TEXTURE_WRAP_T, wrapMode[(int)mTextureUnit.AddressV]); + glTexParameteri(target, GL_TEXTURE_WRAP_R, wrapMode[(int)mTextureUnit.AddressW]); + } + else + { + glBindTexture(GL_TEXTURE_2D, 0); + } + + mTexturesChanged = false; +} + +///////////////////////////////////////////////////////////////////////////// + +extern "C" +{ + +RenderDevice* RenderDevice_New(void* disp, void* window) +{ + RenderDevice *device = new RenderDevice(disp, window); + if (!device->Context) + { + delete device; + return nullptr; + } + else + { + return device; + } +} + +void RenderDevice_Delete(RenderDevice* device) +{ + delete device; +} + +const char* RenderDevice_GetError(RenderDevice* device) +{ + return device->GetError(); +} + +void RenderDevice_SetShader(RenderDevice* device, ShaderName name) +{ + device->SetShader(name); +} + +void RenderDevice_SetUniform(RenderDevice* device, UniformName name, const void* values, int count) +{ + device->SetUniform(name, values, count); +} + +void RenderDevice_SetVertexBuffer(RenderDevice* device, VertexBuffer* buffer) +{ + device->SetVertexBuffer(buffer); +} + +void RenderDevice_SetIndexBuffer(RenderDevice* device, IndexBuffer* buffer) +{ + device->SetIndexBuffer(buffer); +} + +void RenderDevice_SetAlphaBlendEnable(RenderDevice* device, bool value) +{ + device->SetAlphaBlendEnable(value); +} + +void RenderDevice_SetAlphaTestEnable(RenderDevice* device, bool value) +{ + device->SetAlphaTestEnable(value); +} + +void RenderDevice_SetCullMode(RenderDevice* device, Cull mode) +{ + device->SetCullMode(mode); +} + +void RenderDevice_SetBlendOperation(RenderDevice* device, BlendOperation op) +{ + device->SetBlendOperation(op); +} + +void RenderDevice_SetSourceBlend(RenderDevice* device, Blend blend) +{ + device->SetSourceBlend(blend); +} + +void RenderDevice_SetDestinationBlend(RenderDevice* device, Blend blend) +{ + device->SetDestinationBlend(blend); +} + +void RenderDevice_SetFillMode(RenderDevice* device, FillMode mode) +{ + device->SetFillMode(mode); +} + +void RenderDevice_SetMultisampleAntialias(RenderDevice* device, bool value) +{ + device->SetMultisampleAntialias(value); +} + +void RenderDevice_SetZEnable(RenderDevice* device, bool value) +{ + device->SetZEnable(value); +} + +void RenderDevice_SetZWriteEnable(RenderDevice* device, bool value) +{ + device->SetZWriteEnable(value); +} + +void RenderDevice_SetTexture(RenderDevice* device, Texture* texture) +{ + device->SetTexture(texture); +} + +void RenderDevice_SetSamplerFilter(RenderDevice* device, TextureFilter minfilter, TextureFilter magfilter, TextureFilter mipfilter, float maxanisotropy) +{ + device->SetSamplerFilter(minfilter, magfilter, mipfilter, maxanisotropy); +} + +void RenderDevice_SetSamplerState(RenderDevice* device, TextureAddress addressU, TextureAddress addressV, TextureAddress addressW) +{ + device->SetSamplerState(addressU, addressV, addressW); +} + +void RenderDevice_Draw(RenderDevice* device, PrimitiveType type, int startIndex, int primitiveCount) +{ + device->Draw(type, startIndex, primitiveCount); +} + +void RenderDevice_DrawIndexed(RenderDevice* device, PrimitiveType type, int startIndex, int primitiveCount) +{ + device->DrawIndexed(type, startIndex, primitiveCount); +} + +void RenderDevice_DrawData(RenderDevice* device, PrimitiveType type, int startIndex, int primitiveCount, const void* data) +{ + device->DrawData(type, startIndex, primitiveCount, data); +} + +void RenderDevice_StartRendering(RenderDevice* device, bool clear, int backcolor, Texture* target, bool usedepthbuffer) +{ + device->StartRendering(clear, backcolor, target, usedepthbuffer); +} + +void RenderDevice_FinishRendering(RenderDevice* device) +{ + device->FinishRendering(); +} + +void RenderDevice_Present(RenderDevice* device) +{ + device->Present(); +} + +void RenderDevice_ClearTexture(RenderDevice* device, int backcolor, Texture* texture) +{ + device->ClearTexture(backcolor, texture); +} + +void RenderDevice_CopyTexture(RenderDevice* device, Texture* dst, CubeMapFace face) +{ + device->CopyTexture(dst, face); +} + +void RenderDevice_SetVertexBufferData(RenderDevice* device, VertexBuffer* buffer, void* data, int64_t size, VertexFormat format) +{ + device->SetVertexBufferData(buffer, data, size, format); +} + +void RenderDevice_SetVertexBufferSubdata(RenderDevice* device, VertexBuffer* buffer, int64_t destOffset, void* data, int64_t size) +{ + device->SetVertexBufferSubdata(buffer, destOffset, data, size); +} + +void RenderDevice_SetIndexBufferData(RenderDevice* device, IndexBuffer* buffer, void* data, int64_t size) +{ + device->SetIndexBufferData(buffer, data, size); +} + +void RenderDevice_SetPixels(RenderDevice* device, Texture* texture, const void* data) +{ + device->SetPixels(texture, data); +} + +void RenderDevice_SetCubePixels(RenderDevice* device, Texture* texture, CubeMapFace face, const void* data) +{ + device->SetCubePixels(texture, face, data); +} + +} diff --git a/Source/Native/RenderDevice.h b/Source/Native/RenderDevice.h index fce6ab75..692e451f 100644 --- a/Source/Native/RenderDevice.h +++ b/Source/Native/RenderDevice.h @@ -126,7 +126,9 @@ public: void ApplyBlendState(); void ApplyDepthState(); - void CheckError(); + void CheckGLError(); + void SetError(const char* fmt, ...); + const char* GetError(); Shader* GetActiveShader(); @@ -186,6 +188,9 @@ public: bool mContextIsCurrent = false; + char mLastError[4096]; + char mReturnError[4096]; + int mViewportWidth = 0; int mViewportHeight = 0; }; diff --git a/Source/Native/Shader.cpp b/Source/Native/Shader.cpp index 90808b55..6bce91a7 100644 --- a/Source/Native/Shader.cpp +++ b/Source/Native/Shader.cpp @@ -4,31 +4,54 @@ #include "RenderDevice.h" #include -void Shader::Setup(const std::string& vertexShader, const std::string& fragmentShader, bool alphatest) +void Shader::Setup(const std::string& identifier, const std::string& vertexShader, const std::string& fragmentShader, bool alphatest) { + mIdentifier = identifier; mVertexText = vertexShader; mFragmentText = fragmentShader; mAlphatest = alphatest; } -void Shader::Bind() +bool Shader::CheckCompile() { bool firstCall = !mProgramBuilt; if (firstCall) { mProgramBuilt = true; CreateProgram(); + glUseProgram(mProgram); + glUniform1i(glGetUniformLocation(mProgram, "texture1"), 0); + glUseProgram(0); } - if (!mProgram) + return !mErrors.size(); +} + +std::string Shader::GetCompileError() +{ + std::string lines = "Error compiling "; + if (!mVertexShader) + lines += "vertex "; + else if (!mFragmentShader) + lines += "fragment "; + lines += "shader \"" + mIdentifier + "\":\r\n"; + for (auto c : mErrors) + { + if (c == '\r') + continue; + if (c == '\n') + lines += "\r\n"; + else lines += c; + } + return lines; +} + +void Shader::Bind() +{ + if (!mProgram || !mProgramBuilt || mErrors.size()) return; glUseProgram(mProgram); - - if (firstCall) - { - glUniform1i(glGetUniformLocation(mProgram, "texture1"), 0); - } } void Shader::CreateProgram() diff --git a/Source/Native/Shader.h b/Source/Native/Shader.h index e131bcc4..7304fc62 100644 --- a/Source/Native/Shader.h +++ b/Source/Native/Shader.h @@ -11,15 +11,20 @@ public: Shader() = default; void ReleaseResources(); - void Setup(const std::string& vertexShader, const std::string& fragmentShader, bool alphatest); + void Setup(const std::string& identifier, const std::string& vertexShader, const std::string& fragmentShader, bool alphatest); + bool CheckCompile(); void Bind(); + std::string GetIdentifier(); + std::string GetCompileError(); + GLuint UniformLocations[(int)UniformName::NumUniforms] = { 0 }; private: void CreateProgram(); GLuint CompileShader(const std::string& code, GLenum type); + std::string mIdentifier; std::string mVertexText; std::string mFragmentText; bool mAlphatest = false; diff --git a/Source/Native/ShaderManager.cpp b/Source/Native/ShaderManager.cpp index bddd4787..35c9b09c 100644 --- a/Source/Native/ShaderManager.cpp +++ b/Source/Native/ShaderManager.cpp @@ -37,14 +37,42 @@ static const ShaderPair ShaderSources[(int)ShaderName::count] = { { world3D_vs_lightpass, world3D_ps_lightpass } }; +static const std::string ShaderNames[(int)ShaderName::count] = { + "display2d_fsaa", + "display2d_normal", + "display2d_fullbright", + "things2d_thing", + "things2d_sprite", + "things2d_fill", + "plotter", + "world3d_main", + "world3d_fullbright", + "world3d_main_highlight", + "world3d_fullbright_highlight", + "world3d_main_vertexcolor", + "world3d_skybox", + "world3d_main_highlight_vertexcolor", + "world3d_p7", + "world3d_main_fog", + "world3d_p9", + "world3d_main_highlight_fog", + "world3d_p11", + "world3d_main_fog_vertexcolor", + "world3d_p13", + "world3d_main_highlight_fog_vertexcolor", + "world3d_vertex_color", + "world3d_constant_color", + "world3d_lightpass", +}; + ShaderManager::ShaderManager() { for (int i = 0; i < (int)ShaderName::count; i++) { if (ShaderSources[i].vs && ShaderSources[i].ps) { - Shaders[i].Setup(ShaderSources[i].vs, ShaderSources[i].ps, false); - AlphaTestShaders[i].Setup(ShaderSources[i].vs, ShaderSources[i].ps, true); + Shaders[i].Setup(ShaderNames[i], ShaderSources[i].vs, ShaderSources[i].ps, false); + AlphaTestShaders[i].Setup(ShaderNames[i], ShaderSources[i].vs, ShaderSources[i].ps, true); } } } diff --git a/Source/Native/exports.def b/Source/Native/exports.def index 76a903ed..e3504cd1 100644 --- a/Source/Native/exports.def +++ b/Source/Native/exports.def @@ -3,6 +3,7 @@ EXPORTS RenderDevice_New RenderDevice_Delete + RenderDevice_GetError RenderDevice_SetShader RenderDevice_SetUniform RenderDevice_SetVertexBuffer diff --git a/Source/Plugins/VisplaneExplorer/Resources/vpo.dll b/Source/Plugins/VisplaneExplorer/Resources/vpo.dll index c284e8a4..082bae83 100755 Binary files a/Source/Plugins/VisplaneExplorer/Resources/vpo.dll and b/Source/Plugins/VisplaneExplorer/Resources/vpo.dll differ