From 000c7b2263a636eba6a0d52cf77a5ac8f7adf5bb Mon Sep 17 00:00:00 2001 From: Areloch Date: Mon, 15 Apr 2019 23:11:18 -0500 Subject: [PATCH] GL work --- Engine/source/gfx/gl/gfxGLCubemap.cpp | 229 +++- Engine/source/gfx/gl/gfxGLCubemap.h | 33 +- Engine/source/gfx/gl/gfxGLDevice.cpp | 23 + Engine/source/gfx/gl/gfxGLDevice.h | 9 +- Engine/source/gfx/gl/gfxGLShader.cpp | 9 +- Engine/source/gfx/gl/gfxGLStateCache.h | 12 +- Engine/source/gfx/gl/gfxGLUtils.h | 3 + .../source/renderInstance/renderProbeMgr.cpp | 57 +- .../Full/game/core/art/grids/materials.cs | 5 + .../client/lighting/advanced/shaders.cs | 10 +- Templates/Full/game/levels/Pbr Mat Test.mis | 41 +- .../Full/game/shaders/common/gl/lighting.glsl | 169 ++- .../advanced/gl/reflectionProbeArrayP.glsl | 261 +++- gl/Timmy Note.txt | 1 + gl/gfxGLCubemap.cpp | 389 ++++++ gl/gfxGLCubemap.h | 113 ++ gl/gfxGLDevice.cpp | 1029 +++++++++++++++ gl/gfxGLDevice.h | 281 ++++ gl/gfxGLShader.cpp | 1151 +++++++++++++++++ gl/gfxGLStateCache.h | 141 ++ gl/gfxGLUtils.h | 244 ++++ 21 files changed, 4102 insertions(+), 108 deletions(-) create mode 100644 gl/Timmy Note.txt create mode 100644 gl/gfxGLCubemap.cpp create mode 100644 gl/gfxGLCubemap.h create mode 100644 gl/gfxGLDevice.cpp create mode 100644 gl/gfxGLDevice.h create mode 100644 gl/gfxGLShader.cpp create mode 100644 gl/gfxGLStateCache.h create mode 100644 gl/gfxGLUtils.h diff --git a/Engine/source/gfx/gl/gfxGLCubemap.cpp b/Engine/source/gfx/gl/gfxGLCubemap.cpp index d615290d7..7a1c424a9 100644 --- a/Engine/source/gfx/gl/gfxGLCubemap.cpp +++ b/Engine/source/gfx/gl/gfxGLCubemap.cpp @@ -31,7 +31,7 @@ #include "gfx/bitmap/imageUtils.h" -GLenum GFXGLCubemap::faceList[6] = +static GLenum faceList[6] = { GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, @@ -55,6 +55,11 @@ GFXGLCubemap::~GFXGLCubemap() GFXTextureManager::removeEventDelegate( this, &GFXGLCubemap::_onTextureEvent ); } +GLenum GFXGLCubemap::getEnumForFaceNumber(U32 face) +{ + return faceList[face]; +} + void GFXGLCubemap::fillCubeTextures(GFXTexHandle* faces) { AssertFatal( faces, ""); @@ -280,3 +285,225 @@ void GFXGLCubemap::_onTextureEvent( GFXTexCallbackCode code ) else tmResurrect(); } + +U8* GFXGLCubemap::getTextureData(U32 face, U32 mip) +{ + AssertFatal(mMipMapLevels, ""); + mip = (mip < mMipMapLevels) ? mip : 0; + const U32 bytesPerTexel = 4; //TODO make work with more formats!!!!! + const U32 dataSize = ImageUtil::isCompressedFormat(mFaceFormat) + ? getCompressedSurfaceSize(mFaceFormat, mWidth, mHeight, mip) + : (mWidth >> mip) * (mHeight >> mip) * bytesPerTexel; + + U8* data = new U8[dataSize]; + PRESERVE_TEXTURE(GL_TEXTURE_CUBE_MAP); + glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemap); + + if (ImageUtil::isCompressedFormat(mFaceFormat)) + glGetCompressedTexImage(faceList[face], mip, data); + else + glGetTexImage(faceList[face], mip, GFXGLTextureFormat[mFaceFormat], GFXGLTextureType[mFaceFormat], data); + + return data; +} + +//----------------------------------------------------------------------------- +// Cubemap Array +//----------------------------------------------------------------------------- + +GFXGLCubemapArray::GFXGLCubemapArray() +{ +} + +GFXGLCubemapArray::~GFXGLCubemapArray() +{ + glDeleteTextures(1, &mCubemap); +} + +//TODO: really need a common private 'init' function to avoid code double up with these init* functions +void GFXGLCubemapArray::init(GFXCubemapHandle *cubemaps, const U32 cubemapCount) +{ + AssertFatal(cubemaps, "GFXGLCubemapArray- Got null GFXCubemapHandle!"); + AssertFatal(*cubemaps, "GFXGLCubemapArray - Got empty cubemap!"); + + //all cubemaps must be the same size,format and number of mipmaps. Grab the details from the first cubemap + mSize = cubemaps[0]->getSize(); + mFormat = cubemaps[0]->getFormat(); + mMipMapLevels = cubemaps[0]->getMipMapLevels(); + mNumCubemaps = cubemapCount; + const bool isCompressed = ImageUtil::isCompressedFormat(mFormat); + + glGenTextures(1, &mCubemap); + PRESERVE_CUBEMAP_ARRAY_TEXTURE(); + glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, mCubemap); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAX_LEVEL, mMipMapLevels - 1); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + for (U32 i = 0; i < cubemapCount; i++) + { + GFXGLCubemap* glTex = static_cast(cubemaps[i].getPointer()); + for (U32 face = 0; face < 6; face++) + { + for (U32 currentMip = 0; currentMip < mMipMapLevels; currentMip++) + //U32 currentMip = 0; + { + U8 *pixelData = glTex->getTextureData(face, currentMip); + const U32 mipSize = getMax(U32(1), mSize >> currentMip); + /*if (isCompressed) + { + const U32 mipDataSize = getCompressedSurfaceSize(mFormat, mSize, mSize, currentMip); + glCompressedTexImage2D(faceList[face], currentMip, GFXGLTextureInternalFormat[mFormat], mipSize, mipSize, 0, mipDataSize, pixelData); + } + else + {*/ //TODO figure out xyzOffsets + glTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, currentMip, 0, 0, 0, mipSize, mipSize, i * face, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], pixelData); + //} + delete[] pixelData; + } + } + } +} + +//Just allocate the cubemap array but we don't upload any data +void GFXGLCubemapArray::init(const U32 cubemapCount, const U32 cubemapFaceSize, const GFXFormat format) +{ + //all cubemaps must be the same size,format and number of mipmaps. Grab the details from the first cubemap + mSize = cubemapFaceSize; + mFormat = format; + mMipMapLevels = ImageUtil::getMaxMipCount(cubemapFaceSize, cubemapFaceSize); + mNumCubemaps = cubemapCount; + const bool isCompressed = ImageUtil::isCompressedFormat(mFormat); + + glGenTextures(1, &mCubemap); + PRESERVE_CUBEMAP_ARRAY_TEXTURE(); + glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, mCubemap); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAX_LEVEL, mMipMapLevels - 1); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + /*for (U32 i = 0; i < cubemapCount; i++) + { + GFXGLCubemap* glTex = static_cast(cubemaps[i].getPointer()); + for (U32 face = 0; face < 6; face++) + { + for (U32 currentMip = 0; currentMip < mMipMapLevels; currentMip++) + { + U8 *pixelData = glTex->getTextureData(face, currentMip); + const U32 mipSize = getMax(U32(1), mSize >> currentMip); + if (isCompressed) + { + const U32 mipDataSize = getCompressedSurfaceSize(mFormat, mSize, mSize, currentMip); + glCompressedTexImage2D(faceList[face], currentMip, GFXGLTextureInternalFormat[mFormat], mipSize, mipSize, 0, mipDataSize, pixelData); + } + else + { + glTexImage2D(faceList[face], currentMip, GFXGLTextureInternalFormat[mFormat], mipSize, mipSize, + 0, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], pixelData); + } + delete[] pixelData; + } + } + }*/ +} + +void GFXGLCubemapArray::updateTexture(const GFXCubemapHandle &cubemap, const U32 slot) +{ + AssertFatal(slot <= mNumCubemaps, "GFXD3D11CubemapArray::updateTexture - trying to update a cubemap texture that is out of bounds!"); + + const bool isCompressed = ImageUtil::isCompressedFormat(mFormat); + + GFXGLCubemap* glTex = static_cast(cubemap.getPointer()); + for (U32 face = 0; face < 6; face++) + { + for (U32 currentMip = 0; currentMip < mMipMapLevels; currentMip++) + //U32 currentMip = 0; + { + U8 *pixelData = glTex->getTextureData(face, currentMip); + const U32 mipSize = getMax(U32(1), mSize >> currentMip); + /*if (isCompressed) + { + const U32 mipDataSize = getCompressedSurfaceSize(mFormat, mSize, mSize, currentMip); + glCompressedTexImage2D(faceList[face], currentMip, GFXGLTextureInternalFormat[mFormat], mipSize, mipSize, 0, mipDataSize, pixelData); + } + else + {*/ //TODO figure out xyzOffsets + glTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, currentMip, 0, 0, 0, mipSize, mipSize, slot * face, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], pixelData); + //} + delete[] pixelData; + } + } +} + +void GFXGLCubemapArray::copyTo(GFXCubemapArray *pDstCubemap) +{ + AssertFatal(pDstCubemap, "GFXGLCubemapArray::copyTo - Got null GFXCubemapArray"); + + const U32 dstCount = pDstCubemap->getNumCubemaps(); + const GFXFormat dstFmt = pDstCubemap->getFormat(); + const U32 dstSize = pDstCubemap->getSize(); + const U32 dstMips = pDstCubemap->getMipMapLevels(); + + AssertFatal(dstCount > mNumCubemaps, "GFXGLCubemapArray::copyTo - Destination too small"); + AssertFatal(dstFmt == mFormat, "GFXGLCubemapArray::copyTo - Destination format doesn't match"); + AssertFatal(dstSize == mSize, "GFXGLCubemapArray::copyTo - Destination size doesn't match"); + AssertFatal(dstMips == mMipMapLevels, "GFXGLCubemapArray::copyTo - Destination mip levels doesn't match"); + + GFXGLCubemapArray* pDstCube = static_cast(pDstCubemap); + + for (U32 cubeMap = 0; cubeMap < mNumCubemaps; cubeMap++) + { + for (U32 face = 0; face < CubeFaces; face++) + { + for (U32 currentMip = 0; currentMip < mMipMapLevels; currentMip++) + //U32 currentMip = 0; + { + //U8 *pixelData = pDstCube->get->getTextureData(face, currentMip); + const U32 mipSize = getMax(U32(1), mSize >> currentMip); + /*if (isCompressed) + { + const U32 mipDataSize = getCompressedSurfaceSize(mFormat, mSize, mSize, currentMip); + glCompressedTexImage2D(faceList[face], currentMip, GFXGLTextureInternalFormat[mFormat], mipSize, mipSize, 0, mipDataSize, pixelData); + } + else + {*/ //TODO figure out xyzOffsets + glCopyImageSubData(mCubemap, GL_TEXTURE_CUBE_MAP_ARRAY, cubeMap * face, 0, 0, 0, pDstCube->mCubemap, GL_TEXTURE_CUBE_MAP_ARRAY, cubeMap * face, 0, 0, 0, mipSize, mipSize, 6); + //glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, mCubemap); + //glTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, currentMip, 0, 0, 0, mipSize, mipSize, CubeFaces, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], pixelData); + //} + //delete[] pixelData; + } + } + } +} + + +void GFXGLCubemapArray::setToTexUnit(U32 tuNum) +{ + static_cast(getOwningDevice())->setCubemapArrayInternal(tuNum, this); +} + +void GFXGLCubemapArray::bind(U32 textureUnit) const +{ + glActiveTexture(GL_TEXTURE0 + textureUnit); + glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, mCubemap); + static_cast(getOwningDevice())->getOpenglCache()->setCacheBindedTex(textureUnit, GL_TEXTURE_CUBE_MAP_ARRAY, mCubemap); + + GFXGLStateBlockRef sb = static_cast(GFX)->getCurrentStateBlock(); + AssertFatal(sb, "GFXGLCubemap::bind - No active stateblock!"); + if (!sb) + return; + + const GFXSamplerStateDesc& ssd = sb->getDesc().samplers[textureUnit]; + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, minificationFilter(ssd.minFilter, ssd.mipFilter, 0)); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GFXGLTextureFilter[ssd.magFilter]); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S, GFXGLTextureAddress[ssd.addressModeU]); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T, GFXGLTextureAddress[ssd.addressModeV]); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_R, GFXGLTextureAddress[ssd.addressModeW]); +} diff --git a/Engine/source/gfx/gl/gfxGLCubemap.h b/Engine/source/gfx/gl/gfxGLCubemap.h index 47bd72b62..61c8a10bc 100644 --- a/Engine/source/gfx/gl/gfxGLCubemap.h +++ b/Engine/source/gfx/gl/gfxGLCubemap.h @@ -30,6 +30,9 @@ #include "core/resource.h" #endif +const U32 CubeFaces = 6; +const U32 MaxMipMaps = 13; //todo this needs a proper static value somewhere to sync up with other classes like GBitmap + class GFXGLCubemap : public GFXCubemap { @@ -55,7 +58,11 @@ public: /// Called by texCB; this is to ensure that all textures have been resurrected before we attempt to res the cubemap. void tmResurrect(); - static GLenum getEnumForFaceNumber(U32 face) { return faceList[face]; } ///< Performs lookup to get a GLenum for the given face number + static GLenum getEnumForFaceNumber(U32 face);///< Performs lookup to get a GLenum for the given face number + + /// @return An array containing the texture data + /// @note You are responsible for deleting the returned data! (Use delete[]) + U8* getTextureData(U32 face, U32 mip = 0); protected: @@ -85,7 +92,29 @@ protected: virtual void bind(U32 textureUnit) const; ///< Notifies our owning device that we want to be set to the given texture unit (used for GL internal state tracking) void fillCubeTextures(GFXTexHandle* faces); ///< Copies the textures in faces into the cubemap - static GLenum faceList[6]; ///< Lookup table +}; + +class GFXGLCubemapArray : public GFXCubemapArray +{ +public: + GFXGLCubemapArray(); + virtual ~GFXGLCubemapArray(); + //virtual void initStatic(GFXCubemapHandle *cubemaps, const U32 cubemapCount); + virtual void init(GFXCubemapHandle *cubemaps, const U32 cubemapCount); + virtual void init(const U32 cubemapCount, const U32 cubemapFaceSize, const GFXFormat format); + virtual void updateTexture(const GFXCubemapHandle &cubemap, const U32 slot); + virtual void copyTo(GFXCubemapArray *pDstCubemap); + virtual void setToTexUnit(U32 tuNum); + + // GFXResource interface + virtual void zombify() {} + virtual void resurrect() {} + +protected: + friend class GFXGLDevice; + void bind(U32 textureUnit) const; + GLuint mCubemap; ///< Internal GL handle + }; #endif diff --git a/Engine/source/gfx/gl/gfxGLDevice.cpp b/Engine/source/gfx/gl/gfxGLDevice.cpp index eb1d1a677..586d59316 100644 --- a/Engine/source/gfx/gl/gfxGLDevice.cpp +++ b/Engine/source/gfx/gl/gfxGLDevice.cpp @@ -448,6 +448,13 @@ GFXCubemap* GFXGLDevice::createCubemap() return cube; }; +GFXCubemapArray *GFXGLDevice::createCubemapArray() +{ + GFXGLCubemapArray* cubeArray = new GFXGLCubemapArray(); + cubeArray->registerResourceWithDevice(this); + return cubeArray; +} + void GFXGLDevice::endSceneInternal() { // nothing to do for opengl @@ -675,6 +682,22 @@ void GFXGLDevice::setCubemapInternal(U32 textureUnit, const GFXGLCubemap* textur } } +void GFXGLDevice::setCubemapArrayInternal(U32 textureUnit, const GFXGLCubemapArray* texture) +{ + if (texture) + { + mActiveTextureType[textureUnit] = GL_TEXTURE_CUBE_MAP_ARRAY; + texture->bind(textureUnit); + } + else if (mActiveTextureType[textureUnit] != GL_ZERO) + { + glActiveTexture(GL_TEXTURE0 + textureUnit); + glBindTexture(mActiveTextureType[textureUnit], 0); + getOpenglCache()->setCacheBindedTex(textureUnit, mActiveTextureType[textureUnit], 0); + mActiveTextureType[textureUnit] = GL_ZERO; + } +} + void GFXGLDevice::setMatrix( GFXMatrixType mtype, const MatrixF &mat ) { // ONLY NEEDED ON FFP diff --git a/Engine/source/gfx/gl/gfxGLDevice.h b/Engine/source/gfx/gl/gfxGLDevice.h index 29740c1c5..179957db2 100644 --- a/Engine/source/gfx/gl/gfxGLDevice.h +++ b/Engine/source/gfx/gl/gfxGLDevice.h @@ -39,6 +39,7 @@ class GFXGLVertexBuffer; class GFXGLPrimitiveBuffer; class GFXGLTextureTarget; class GFXGLCubemap; +class GFXGLCubemapArray; class GFXGLStateCache; class GFXGLVertexDecl; @@ -81,7 +82,7 @@ public: virtual U32 getTotalVideoMemory(); virtual GFXCubemap * createCubemap(); - virtual GFXCubemapArray *createCubemapArray() { return NULL; } //TODO: implement + virtual GFXCubemapArray *createCubemapArray(); virtual F32 getFillConventionOffset() const { return 0.0f; } @@ -170,7 +171,8 @@ protected: virtual void setShaderConstBufferInternal(GFXShaderConstBuffer* buffer); virtual void setTextureInternal(U32 textureUnit, const GFXTextureObject*texture); - virtual void setCubemapInternal(U32 cubemap, const GFXGLCubemap* texture); + virtual void setCubemapInternal(U32 textureUnit, const GFXGLCubemap* texture); + virtual void setCubemapArrayInternal(U32 textureUnit, const GFXGLCubemapArray* texture); virtual void setLightInternal(U32 lightStage, const GFXLightInfo light, bool lightEnable); virtual void setLightMaterialInternal(const GFXLightMaterial mat); @@ -207,11 +209,12 @@ private: friend class GFXGLTextureObject; friend class GFXGLCubemap; + friend class GFXGLCubemapArray; friend class GFXGLWindowTarget; friend class GFXGLPrimitiveBuffer; friend class GFXGLVertexBuffer; - static GFXAdapter::CreateDeviceInstanceDelegate mCreateDeviceInstance; + static GFXAdapter::CreateDeviceInstanceDelegate mCreateDeviceInstance; U32 mAdapterIndex; diff --git a/Engine/source/gfx/gl/gfxGLShader.cpp b/Engine/source/gfx/gl/gfxGLShader.cpp index 55f05543a..70db7c1c1 100644 --- a/Engine/source/gfx/gl/gfxGLShader.cpp +++ b/Engine/source/gfx/gl/gfxGLShader.cpp @@ -80,6 +80,7 @@ static U32 shaderConstTypeSize(GFXShaderConstType type) case GFXSCT_Int: case GFXSCT_Sampler: case GFXSCT_SamplerCube: + case GFXSCT_SamplerCubeArray: return 4; case GFXSCT_Float2: case GFXSCT_Int2: @@ -625,6 +626,9 @@ void GFXGLShader::initConstantDescs() case GL_SAMPLER_CUBE: desc.constType = GFXSCT_SamplerCube; break; + case GL_SAMPLER_CUBE_MAP_ARRAY: + desc.constType = GFXSCT_SamplerCubeArray; + break; default: AssertFatal(false, "GFXGLShader::initConstantDescs - unrecognized uniform type"); // If we don't recognize the constant don't add its description. @@ -656,7 +660,7 @@ void GFXGLShader::initHandles() HandleMap::Iterator handle = mHandles.find(desc.name); S32 sampler = -1; - if(desc.constType == GFXSCT_Sampler || desc.constType == GFXSCT_SamplerCube) + if(desc.constType == GFXSCT_Sampler || desc.constType == GFXSCT_SamplerCube || desc.constType == GFXSCT_SamplerCubeArray) { S32 idx = mSamplerNamesOrdered.find_next(desc.name); AssertFatal(idx != -1, ""); @@ -699,7 +703,7 @@ void GFXGLShader::initHandles() for (HandleMap::Iterator iter = mHandles.begin(); iter != mHandles.end(); ++iter) { GFXGLShaderConstHandle* handle = iter->value; - if(handle->isValid() && (handle->getType() == GFXSCT_Sampler || handle->getType() == GFXSCT_SamplerCube)) + if(handle->isValid() && (handle->getType() == GFXSCT_Sampler || handle->getType() == GFXSCT_SamplerCube || handle->getType() == GFXSCT_SamplerCubeArray)) { // Set sampler number on our program. glUniform1i(handle->mLocation, handle->mSamplerNum); @@ -831,6 +835,7 @@ void GFXGLShader::setConstantsFromBuffer(GFXGLShaderConstBuffer* buffer) case GFXSCT_Int: case GFXSCT_Sampler: case GFXSCT_SamplerCube: + case GFXSCT_SamplerCubeArray: glUniform1iv(handle->mLocation, handle->mDesc.arraySize, (GLint*)(mConstBuffer + handle->mOffset)); break; case GFXSCT_Int2: diff --git a/Engine/source/gfx/gl/gfxGLStateCache.h b/Engine/source/gfx/gl/gfxGLStateCache.h index 172072983..24b3179e4 100644 --- a/Engine/source/gfx/gl/gfxGLStateCache.h +++ b/Engine/source/gfx/gl/gfxGLStateCache.h @@ -20,11 +20,11 @@ public: class TextureUnit { public: - TextureUnit() : mTexture1D(0), mTexture2D(0), mTexture3D(0), mTextureCube(0) + TextureUnit() : mTexture1D(0), mTexture2D(0), mTexture3D(0), mTextureCube(0), mTextureCubeArray(0) { } - GLuint mTexture1D, mTexture2D, mTexture3D, mTextureCube; + GLuint mTexture1D, mTexture2D, mTexture3D, mTextureCube, mTextureCubeArray; }; /// after glBindTexture @@ -45,6 +45,9 @@ public: case GL_TEXTURE_CUBE_MAP: mTextureUnits[mActiveTexture].mTextureCube = handle; break; + case GL_TEXTURE_CUBE_MAP_ARRAY: + mTextureUnits[mActiveTexture].mTextureCubeArray = handle; + break; default: AssertFatal(0, avar("GFXGLStateCache::setCacheBindedTex - binding (%x) not supported.", biding) ); return; @@ -68,6 +71,9 @@ public: case GL_TEXTURE_CUBE_MAP: mTextureUnits[mActiveTexture].mTextureCube = handle; break; + case GL_TEXTURE_CUBE_MAP_ARRAY: + mTextureUnits[mActiveTexture].mTextureCubeArray = handle; + break; case GL_FRAMEBUFFER: mBindedFBO_W = mBindedFBO_R = handle; break; @@ -101,6 +107,8 @@ public: return mTextureUnits[mActiveTexture].mTexture1D; case GL_TEXTURE_CUBE_MAP: return mTextureUnits[mActiveTexture].mTextureCube; + case GL_TEXTURE_CUBE_MAP_ARRAY: + return mTextureUnits[mActiveTexture].mTextureCubeArray; case GL_DRAW_FRAMEBUFFER: return mBindedFBO_W; case GL_READ_FRAMEBUFFER: diff --git a/Engine/source/gfx/gl/gfxGLUtils.h b/Engine/source/gfx/gl/gfxGLUtils.h index 2cb6450a6..34eabcbd8 100644 --- a/Engine/source/gfx/gl/gfxGLUtils.h +++ b/Engine/source/gfx/gl/gfxGLUtils.h @@ -191,6 +191,9 @@ GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_3D, GL_TEXTU #define PRESERVE_CUBEMAP_TEXTURE() \ GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP, (GFXGLPreserveInteger::BindFn)glBindTexture) +#define PRESERVE_CUBEMAP_ARRAY_TEXTURE() \ +GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_BINDING_CUBE_MAP_ARRAY, (GFXGLPreserveInteger::BindFn)glBindTexture) + #define _GET_TEXTURE_BINDING(binding) \ binding == GL_TEXTURE_2D ? GL_TEXTURE_BINDING_2D : (binding == GL_TEXTURE_3D ? GL_TEXTURE_BINDING_3D : GL_TEXTURE_BINDING_1D ) diff --git a/Engine/source/renderInstance/renderProbeMgr.cpp b/Engine/source/renderInstance/renderProbeMgr.cpp index 0f884bf98..048f9a206 100644 --- a/Engine/source/renderInstance/renderProbeMgr.cpp +++ b/Engine/source/renderInstance/renderProbeMgr.cpp @@ -423,7 +423,7 @@ void RenderProbeMgr::_setupStaticParameters() if (!curEntry.mIsEnabled) continue; - if (curEntry.mIsSkylight) + if (curEntry.mProbeShapeType == ProbeRenderInst::ProbeShapeType::Skylight || curEntry.mIsSkylight) { skylightPos = curEntry.getPosition(); skylightPrefilterMap = curEntry.mPrefilterCubemap; @@ -680,11 +680,13 @@ void RenderProbeMgr::_update4ProbeConsts(const SceneData &sgData, { if (curEntry.mPrefilterCubemap.isValid() && curEntry.mPrefilterCubemap.isValid()) { - U32 specSample = probeShaderConsts->mSkylightSpecularMap->getSamplerRegister(); - U32 irradSample = probeShaderConsts->mSkylightIrradMap->getSamplerRegister(); + S32 specSample = probeShaderConsts->mSkylightSpecularMap->getSamplerRegister(); + if (specSample != -1) + GFX->setCubeTexture(specSample, curEntry.mPrefilterCubemap); - GFX->setCubeTexture(probeShaderConsts->mSkylightSpecularMap->getSamplerRegister(), curEntry.mPrefilterCubemap); - GFX->setCubeTexture(probeShaderConsts->mSkylightIrradMap->getSamplerRegister(), curEntry.mIrradianceCubemap); + S32 irradSample = probeShaderConsts->mSkylightIrradMap->getSamplerRegister(); + if (irradSample != -1) + GFX->setCubeTexture(irradSample, curEntry.mIrradianceCubemap); shaderConsts->setSafe(probeShaderConsts->mHasSkylight, 1.0f); hasSkylight = true; @@ -718,8 +720,13 @@ void RenderProbeMgr::_update4ProbeConsts(const SceneData &sgData, shaderConsts->setSafe(probeShaderConsts->mProbeBoxMaxSC, probeBoxMaxArray); shaderConsts->setSafe(probeShaderConsts->mProbeConfigDataSC, probeConfigArray); - GFX->setCubeArrayTexture(probeShaderConsts->mProbeSpecularCubemapSC->getSamplerRegister(), mPrefilterArray); - GFX->setCubeArrayTexture(probeShaderConsts->mProbeIrradianceCubemapSC->getSamplerRegister(), mIrradianceArray); + S32 specSample = probeShaderConsts->mProbeSpecularCubemapSC->getSamplerRegister(); + if (specSample != -1) + GFX->setCubeArrayTexture(specSample, mPrefilterArray); + + S32 irradSample = probeShaderConsts->mProbeIrradianceCubemapSC->getSamplerRegister(); + if (irradSample != -1) + GFX->setCubeArrayTexture(irradSample, mIrradianceArray); if (!hasSkylight) shaderConsts->setSafe(probeShaderConsts->mHasSkylight, 0.0f); @@ -779,8 +786,7 @@ void RenderProbeMgr::render( SceneRenderState *state ) //updateProbes(); // Early out if nothing to draw. - if (!ProbeRenderInst::all.size() || !RenderProbeMgr::smRenderReflectionProbes || !state->isDiffusePass() || (mEffectiveProbeCount == 0 - || mCubeMapCount != 0 && !hasSkylight)) + if (!RenderProbeMgr::smRenderReflectionProbes || !state->isDiffusePass() || (!ProbeRenderInst::all.size() || mEffectiveProbeCount == 0 || mCubeMapCount != 0 ) && !hasSkylight) { getProbeArrayEffect()->setSkip(true); return; @@ -793,6 +799,19 @@ void RenderProbeMgr::render( SceneRenderState *state ) // Initialize and set the per-frame parameters after getting // the vector light material as we use lazy creation. //_setupPerFrameParameters(state); + + //Visualization + String useDebugAtten = Con::getVariable("$Probes::showAttenuation", "0"); + mProbeArrayEffect->setShaderMacro("DEBUGVIZ_ATTENUATION", useDebugAtten); + + String useDebugSpecCubemap = Con::getVariable("$Probes::showSpecularCubemaps", "0"); + mProbeArrayEffect->setShaderMacro("DEBUGVIZ_SPECCUBEMAP", useDebugSpecCubemap); + + String useDebugDiffuseCubemap = Con::getVariable("$Probes::showDiffuseCubemaps", "0"); + mProbeArrayEffect->setShaderMacro("DEBUGVIZ_DIFFCUBEMAP", useDebugDiffuseCubemap); + + String useDebugContrib = Con::getVariable("$Probes::showProbeContrib", "0"); + mProbeArrayEffect->setShaderMacro("DEBUGVIZ_CONTRIB", useDebugContrib); //Array rendering //U32 probeCount = ProbeRenderInst::all.size(); @@ -804,23 +823,14 @@ void RenderProbeMgr::render( SceneRenderState *state ) mProbeArrayEffect->setCubemapTexture(7, skylightIrradMap); } + mProbeArrayEffect->setShaderConst("$numProbes", (float)mEffectiveProbeCount); + + mProbeArrayEffect->setShaderConst("$cubeMips", (float)mMipCount); if (mEffectiveProbeCount != 0) { mProbeArrayEffect->setCubemapArrayTexture(4, mPrefilterArray); mProbeArrayEffect->setCubemapArrayTexture(5, mIrradianceArray); - String useDebugAtten = Con::getVariable("$Probes::showAttenuation", "0"); - mProbeArrayEffect->setShaderMacro("DEBUGVIZ_ATTENUATION", useDebugAtten); - - String useDebugSpecCubemap = Con::getVariable("$Probes::showSpecularCubemaps", "0"); - mProbeArrayEffect->setShaderMacro("DEBUGVIZ_SPECCUBEMAP", useDebugSpecCubemap); - - String useDebugDiffuseCubemap = Con::getVariable("$Probes::showDiffuseCubemaps", "0"); - mProbeArrayEffect->setShaderMacro("DEBUGVIZ_DIFFCUBEMAP", useDebugDiffuseCubemap); - - String useDebugContrib = Con::getVariable("$Probes::showProbeContrib", "0"); - mProbeArrayEffect->setShaderMacro("DEBUGVIZ_CONTRIB", useDebugContrib); - if (useDebugContrib == String("1")) { MRandomLCG RandomGen; @@ -841,15 +851,12 @@ void RenderProbeMgr::render( SceneRenderState *state ) else if (i == 2) contribColors[i] = Point4F(0, 0, 1, 1); else - contribColors[i] = Point4F(RandomGen.randF(0, 1), RandomGen.randF(0, 1), RandomGen.randF(0, 1),1); + contribColors[i] = Point4F(RandomGen.randF(0, 1), RandomGen.randF(0, 1), RandomGen.randF(0, 1), 1); } mProbeArrayEffect->setShaderConst("$probeContribColors", contribColors); } - - mProbeArrayEffect->setShaderConst("$cubeMips", (float)mMipCount); - mProbeArrayEffect->setShaderConst("$numProbes", (float)mEffectiveProbeCount); mProbeArrayEffect->setShaderConst("$inProbePosArray", probePositionsData); mProbeArrayEffect->setShaderConst("$inRefPosArray", probeRefPositionsData); mProbeArrayEffect->setShaderConst("$worldToObjArray", probeWorldToObjData); diff --git a/Templates/Full/game/core/art/grids/materials.cs b/Templates/Full/game/core/art/grids/materials.cs index 00039aa17..20f4a07a0 100644 --- a/Templates/Full/game/core/art/grids/materials.cs +++ b/Templates/Full/game/core/art/grids/materials.cs @@ -36,6 +36,11 @@ singleton Material( Grid512_Blue_Mat ) metalness[0] = "0.803922"; translucent = "1"; translucentBlendOp = "Add"; + normalMap[0] = "art/pbr/floor/FloorEbony_normal.png"; + invertSmoothness[0] = "1"; + roughMap[0] = "art/pbr/floor/FloorEbony_rough.png"; + aoMap[0] = "art/pbr/floor/FloorEbony_ao.png"; + metalMap[0] = "art/pbr/floor/FloorEbony_metal.png"; }; singleton Material( Grid512_ForestGreen_Mat ) diff --git a/Templates/Full/game/core/scripts/client/lighting/advanced/shaders.cs b/Templates/Full/game/core/scripts/client/lighting/advanced/shaders.cs index 985c8dd1d..0418210d6 100644 --- a/Templates/Full/game/core/scripts/client/lighting/advanced/shaders.cs +++ b/Templates/Full/game/core/scripts/client/lighting/advanced/shaders.cs @@ -310,15 +310,17 @@ singleton ShaderData( PFX_ReflectionProbeArray ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/lighting/advanced/reflectionProbeArrayP.hlsl"; - //OGLVertexShaderFile = "shaders/common/postFx/gl//postFxV.glsl"; - //OGLPixelShaderFile = "shaders/common/postFx/gl/passthruP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/reflectionProbeArrayP.glsl"; samplerNames[0] = "$deferredBuffer"; samplerNames[1] = "$colorBuffer"; samplerNames[2] = "$matInfoBuffer"; samplerNames[3] = "$BRDFTexture"; - samplerNames[4] = "$cubeMap"; - samplerNames[5] = "$irradianceCubemap"; + samplerNames[4] = "$specularCubemapAR"; + samplerNames[5] = "$irradianceCubemapAR"; + samplerNames[6] = "$skylightSpecularMap"; + samplerNames[7] = "$skylightIrradMap"; pixVersion = 2.0; }; diff --git a/Templates/Full/game/levels/Pbr Mat Test.mis b/Templates/Full/game/levels/Pbr Mat Test.mis index a8c37ab32..f6f3c8380 100644 --- a/Templates/Full/game/levels/Pbr Mat Test.mis +++ b/Templates/Full/game/levels/Pbr Mat Test.mis @@ -99,18 +99,17 @@ new SimGroup(MissionGroup) { }; new Skylight(theSkyLight) { enabled = "1"; - ProbeShape = "Box"; - radius = "10"; - posOffset = "0 0 0"; ReflectionMode = "Baked Cubemap"; - reflectionPath = "levels/Timmy Test/probes/"; - Bake = "0"; - position = "8.74661 10.1457 2.48852"; + position = "8.74661 10.1457 2.94337"; rotation = "1 0 0 0"; - scale = "1 1 1"; canSave = "1"; canSaveDynamicFields = "1"; persistentId = "247d7009-db8a-11e8-87b8-ed691a78e155"; + posOffset = "0 0 0"; + ProbeShape = "Box"; + radius = "10"; + reflectionPath = "levels/Timmy Test/probes/"; + scale = "1 1 1"; }; new TSStatic() { shapeName = "art/shapes/material_ball/material_ball.dae"; @@ -351,5 +350,33 @@ new SimGroup(MissionGroup) { canSave = "1"; canSaveDynamicFields = "1"; }; + new ConvexShape() { + Material = "Grid512_Blue_Mat"; + position = "-6.41329 13.7734 2.12278"; + rotation = "0 0 1 3.19212"; + scale = "1 1 1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + surface = "0 0 0 1 0 0 0.384079"; + surface = "0 1 0 0 0 0 -0.384079"; + surface = "0.707107 0 0 0.707107 0 0.285194 0"; + surface = "0 0.707107 -0.707107 0 0 -0.285194 -1.42109e-14"; + surface = "0.5 0.5 -0.5 0.5 -0.758333 0 -1.07696e-07"; + surface = "0.5 -0.5 0.5 0.5 0.758333 0 -1.07696e-07"; + }; + new BoxEnvironmentProbe() { + enabled = "0"; + refOffset = "0 0 0"; + refScale = "10 10 10"; + ReflectionMode = "Baked Cubemap"; + position = "-11.7544 15.2634 4.67576"; + rotation = "1 0 0 0"; + scale = "10 10 10"; + canSave = "1"; + canSaveDynamicFields = "1"; + persistentId = "8c7e1f23-5f1c-11e9-8089-c88cdaba85a3"; + attenuation = "1"; + }; }; //--- OBJECT WRITE END --- diff --git a/Templates/Full/game/shaders/common/gl/lighting.glsl b/Templates/Full/game/shaders/common/gl/lighting.glsl index 24768221f..d37b25224 100644 --- a/Templates/Full/game/shaders/common/gl/lighting.glsl +++ b/Templates/Full/game/shaders/common/gl/lighting.glsl @@ -44,6 +44,9 @@ uniform vec4 albedo; #endif // !TORQUE_SHADERGEN +#define MAX_PROBES 50 +#define MAX_FORWARD_PROBES 4 + vec3 getDistanceVectorToPlane( vec3 origin, vec3 direction, vec4 plane ) { float denum = dot( plane.xyz, direction.xyz ); @@ -119,15 +122,15 @@ void Surface::Update() F = F_Schlick(f0, f90, NdotV); } -Surface createSurface(vec4 gbuffer0, sampler2D gbufferTex1, sampler2D gbufferTex2, in vec2 uv, in vec3 wsEyePos, in vec3 wsEyeRay, in mat4 invView) +Surface createSurface(vec4 normDepth, sampler2D colorBuffer, sampler2D matInfoBuffer, in vec2 uv, in vec3 wsEyePos, in vec3 wsEyeRay, in mat4 invView) { Surface surface;// = Surface(); - vec4 gbuffer1 = texture(gbufferTex1, uv); - vec4 gbuffer2 = texture(gbufferTex2, uv); - surface.depth = gbuffer0.a; + vec4 gbuffer1 = texture(colorBuffer, uv); + vec4 gbuffer2 = texture(matInfoBuffer, uv); + surface.depth = normDepth.a; surface.P = wsEyePos + wsEyeRay * surface.depth; - surface.N = tMul(invView, vec4(gbuffer0.xyz,0)).xyz; //TODO move t3d to use WS normals + surface.N = tMul(invView, vec4(normDepth.xyz,0)).xyz; //TODO move t3d to use WS normals surface.V = normalize(wsEyePos - surface.P); surface.baseColor = gbuffer1; const float minRoughness=1e-4; @@ -266,4 +269,158 @@ vec3 directSpecular(vec3 N, vec3 V, vec3 L, float roughness, float F0) float specular = dotNL * D * F * vis; return vec3(specular,specular,specular); -} \ No newline at end of file +} + +//Probe IBL stuff +float defineSphereSpaceInfluence(vec3 wsPosition, vec3 wsProbePosition, float radius) +{ + vec3 L = wsProbePosition.xyz - wsPosition; + float contribution = 1.0 - length(L) / radius; + return contribution; +} + +float getDistBoxToPoint(vec3 pt, vec3 extents) +{ + vec3 d = max(max(-extents - pt, 0), pt - extents); + return max(max(d.x, d.y), d.z); +} + +float defineBoxSpaceInfluence(vec3 wsPosition, mat4 worldToObj, float attenuation) +{ + vec3 surfPosLS = tMul(worldToObj, vec4(wsPosition, 1.0)).xyz; + float atten = 1.0 - attenuation; + float baseVal = 0.25; + float dist = getDistBoxToPoint(surfPosLS, vec3(baseVal, baseVal, baseVal)); + return saturate(smoothstep(baseVal + 0.0001, atten*baseVal, dist)); +} + +// Box Projected IBL Lighting +// Based on: http://www.gamedev.net/topic/568829-box-projected-cubemap-environment-mapping/ +// and https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ +vec3 boxProject(vec3 wsPosition, vec3 wsReflectVec, mat4 worldToObj, vec3 bbMin, vec3 bbMax, vec3 refPosition) +{ + vec3 RayLS = tMul(worldToObj, vec4(wsReflectVec, 0.0)).xyz; + vec3 PositionLS = tMul(worldToObj, vec4(wsPosition, 1.0)).xyz; + + vec3 unit = bbMax.xyz - bbMin.xyz; + vec3 plane1vec = (unit / 2 - PositionLS) / RayLS; + vec3 plane2vec = (-unit / 2 - PositionLS) / RayLS; + vec3 furthestPlane = max(plane1vec, plane2vec); + float dist = min(min(furthestPlane.x, furthestPlane.y), furthestPlane.z); + vec3 posonbox = wsPosition + wsReflectVec * dist; + + return posonbox - refPosition.xyz; +} + +/*vec4 computeForwardProbes(Surface surface, + float cubeMips, float numProbes, mat4 worldToObjArray[MAX_FORWARD_PROBES], vec4 probeConfigData[MAX_FORWARD_PROBES], + vec4 inProbePosArray[MAX_FORWARD_PROBES], vec4 bbMinArray[MAX_FORWARD_PROBES], vec4 bbMaxArray[MAX_FORWARD_PROBES], vec4 inRefPosArray[MAX_FORWARD_PROBES], + float hasSkylight, samplerCube skylightIrradMap, samplerCube skylightSpecularMap, + sampler2D BRDFTexture, samplerCubeArray irradianceCubemapAR, + samplerCubeArray specularCubemapAR) +{ + return vec4(0,0,0,1); + + int i = 0; + float blendFactor[MAX_FORWARD_PROBES]; + float blendSum = 0; + float blendFacSum = 0; + float invBlendSum = 0; + float probehits = 0; + //Set up our struct data + float contribution[MAX_FORWARD_PROBES]; + for (i = 0; i < numProbes; ++i) + { + contribution[i] = 0; + + if (probeConfigData[i].r == 0) //box + { + contribution[i] = defineBoxSpaceInfluence(surface.P, worldToObjArray[i], probeConfigData[i].b); + if (contribution[i] > 0.0) + probehits++; + } + else if (probeConfigData[i].r == 1) //sphere + { + contribution[i] = defineSphereSpaceInfluence(surface.P, inProbePosArray[i].xyz, probeConfigData[i].g); + if (contribution[i] > 0.0) + probehits++; + } + + contribution[i] = max(contribution[i], 0); + + blendSum += contribution[i]; + invBlendSum += (1.0f - contribution[i]); + } + + if (probehits > 1.0) + { + for (i = 0; i < numProbes; i++) + { + blendFactor[i] = ((contribution[i] / blendSum)) / probehits; + blendFactor[i] *= ((contribution[i]) / invBlendSum); + blendFactor[i] = saturate(blendFactor[i]); + blendFacSum += blendFactor[i]; + } + + // Normalize blendVal + if (blendFacSum == 0.0f) // Possible with custom weight + { + blendFacSum = 1.0f; + } + + float invBlendSumWeighted = 1.0f / blendFacSum; + for (i = 0; i < numProbes; ++i) + { + blendFactor[i] *= invBlendSumWeighted; + contribution[i] *= blendFactor[i]; + //alpha -= contribution[i]; + } + } + //else + // alpha -= blendSum; + + vec3 irradiance = vec3(0, 0, 0); + vec3 specular = vec3(0, 0, 0); + + // Radiance (Specular) + float lod = surface.roughness*cubeMips; + + float alpha = 1; + for (i = 0; i < numProbes; ++i) + { + float contrib = contribution[i]; + if (contrib != 0) + { + int cubemapIdx = probeConfigData[i].a; + vec3 dir = boxProject(surface.P, surface.R, worldToObjArray[i], bbMinArray[i].xyz, bbMaxArray[i].xyz, inRefPosArray[i].xyz); + + irradiance += textureLod(irradianceCubemapAR, vec4(dir, cubemapIdx), 0).xyz * contrib; + specular += textureLod(specularCubemapAR, vec4(dir, cubemapIdx), lod).xyz * contrib; + alpha -= contrib; + } + } + + if (hasSkylight == 1 && alpha > 0.001) + { + irradiance += textureLod(skylightIrradMap, surface.R, 0).xyz * alpha; + specular += textureLod(skylightSpecularMap, surface.R, lod).xyz * alpha; + } + + vec3 F = FresnelSchlickRoughness(surface.NdotV, surface.f0, surface.roughness); + + //energy conservation + vec3 kD = 1.0.xxx - F; + kD *= 1.0 - surface.metalness; + + //apply brdf + //Do it once to save on texture samples + vec2 brdf = texture(BRDFTexture, vec2(surface.roughness, surface.NdotV)).xy; + specular *= brdf.x * F + brdf.y; + + //final diffuse color + vec3 diffuse = kD * irradiance * surface.baseColor.rgb; + vec4 finalColor = vec4(diffuse + specular * surface.ao, 1.0); + + finalColor = vec4(irradiance.rgb,1); + return finalColor; +}*/ \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/lighting/advanced/gl/reflectionProbeArrayP.glsl b/Templates/Full/game/shaders/common/lighting/advanced/gl/reflectionProbeArrayP.glsl index 8edf5c3e6..aeb89678b 100644 --- a/Templates/Full/game/shaders/common/lighting/advanced/gl/reflectionProbeArrayP.glsl +++ b/Templates/Full/game/shaders/common/lighting/advanced/gl/reflectionProbeArrayP.glsl @@ -1,63 +1,212 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- +#include "../../../gl/hlslCompat.glsl" +#include "../../../postFx/gl/postFx.glsl" +#include "../../../gl/torque.glsl" +#include "shadergen:/autogenConditioners.h" +#include "../../../gl/lighting.glsl" -#include "../../torque.hlsl" +#line 7 -struct ConnectData +uniform sampler2D deferredBuffer; +uniform sampler2D colorBuffer; +uniform sampler2D matInfoBuffer; +uniform sampler2D BRDFTexture; + +uniform vec4 rtParams0; +uniform vec4 vsFarPlane; +uniform mat4 cameraToWorld; +uniform vec3 eyePosWorld; + +//cubemap arrays require all the same size. so shared mips# value +uniform float cubeMips; + +uniform float numProbes; +uniform samplerCubeArray specularCubemapAR; +uniform samplerCubeArray irradianceCubemapAR; + +uniform vec4 inProbePosArray[MAX_PROBES]; +uniform vec4 inRefPosArray[MAX_PROBES]; +uniform mat4 worldToObjArray[MAX_PROBES]; +uniform vec4 bbMinArray[MAX_PROBES]; +uniform vec4 bbMaxArray[MAX_PROBES]; +uniform vec4 probeConfigData[MAX_PROBES]; //r,g,b/mode,radius,atten + +#if DEBUGVIZ_CONTRIB +uniform vec4 probeContribColors[MAX_PROBES]; +#endif + +uniform samplerCube skylightSpecularMap; +uniform samplerCube skylightIrradMap; +uniform float hasSkylight; + +out vec4 OUT_col; + +void main() { - float4 hpos : TORQUE_POSITION; - float2 uv : TEXCOORD; -}; + //unpack normal and linear depth + vec4 normDepth = deferredUncondition(deferredBuffer, IN_uv0.xy); -uniform int face; + //create surface + Surface surface = createSurface(normDepth, colorBuffer, matInfoBuffer, IN_uv0.xy, eyePosWorld, IN_wsEyeRay, cameraToWorld); -TORQUE_UNIFORM_SAMPLERCUBE(environmentMap, 0); + //early out if emissive + if (getFlag(surface.matFlag, 0)) + { + discard; + } -float4 main(ConnectData IN) : TORQUE_TARGET0 -{ - float3 N = getCubeDir(face,IN.uv); - float3 irradiance = 0; - - // tangent space calculation from origin point - float3 up = float3(0.0, 0.0, 1.0); - float3 right = cross(up, N); - up = cross(N, right); - - float sampleDelta = 0.025; - int nrSamples = 0; - for(float phi = 0.0; phi < M_2PI_F; phi += sampleDelta) - { - for(float theta = 0.0; theta < M_HALFPI_F; theta += sampleDelta) - { - // spherical to cartesian (in tangent space) - float3 tangentSample = float3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)); - // tangent space to world - float3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N; + float alpha = 1; - irradiance += TORQUE_TEXCUBE(environmentMap, sampleVec).rgb * cos(theta) * sin(theta); - nrSamples++; - } - } - irradiance = M_PI_F * irradiance * (1.0 / float(nrSamples)); - - return float4(irradiance, 1.0); -} \ No newline at end of file + int i = 0; + float blendFactor[MAX_PROBES]; + float blendSum = 0; + float blendFacSum = 0; + float invBlendSum = 0; + float probehits = 0; + //Set up our struct data + float contribution[MAX_PROBES]; + if (alpha > 0) + { + //Process prooooobes + for (i = 0; i < numProbes; ++i) + { + contribution[i] = 0; + + if (probeConfigData[i].r == 0) //box + { + contribution[i] = defineBoxSpaceInfluence(surface.P, worldToObjArray[i], probeConfigData[i].b); + if (contribution[i]>0.0) + probehits++; + } + else if (probeConfigData[i].r == 1) //sphere + { + contribution[i] = defineSphereSpaceInfluence(surface.P, inProbePosArray[i].xyz, probeConfigData[i].g); + if (contribution[i]>0.0) + probehits++; + } + + contribution[i] = max(contribution[i],0); + + blendSum += contribution[i]; + invBlendSum += (1.0f - contribution[i]); + } + // Weight0 = normalized NDF, inverted to have 1 at center, 0 at boundary. + // And as we invert, we need to divide by Num-1 to stay normalized (else sum is > 1). + // respect constraint B. + // Weight1 = normalized inverted NDF, so we have 1 at center, 0 at boundary + // and respect constraint A. + + if (probehits>1.0) + { + for (i = 0; i < numProbes; i++) + { + blendFactor[i] = ((contribution[i] / blendSum)) / probehits; + blendFactor[i] *= ((contribution[i]) / invBlendSum); + blendFactor[i] = saturate(blendFactor[i]); + blendFacSum += blendFactor[i]; + } + + // Normalize blendVal +#if DEBUGVIZ_ATTENUATION == 0 //this can likely be removed when we fix the above normalization behavior + if (blendFacSum == 0.0f) // Possible with custom weight + { + blendFacSum = 1.0f; + } +#endif + + float invBlendSumWeighted = 1.0f / blendFacSum; + for (i = 0; i < numProbes; ++i) + { + blendFactor[i] *= invBlendSumWeighted; + contribution[i] *= blendFactor[i]; + alpha -= contribution[i]; + } + } + else + alpha -= blendSum; + +#if DEBUGVIZ_ATTENUATION == 1 + float contribAlpha = 1; + for (i = 0; i < numProbes; ++i) + { + contribAlpha -= contribution[i]; + } + + OUT_col = vec4(1 - contribAlpha, 1 - contribAlpha, 1 - contribAlpha, 1); + return; +#endif + +#if DEBUGVIZ_CONTRIB == 1 + vec3 finalContribColor = vec3(0, 0, 0); + float contribAlpha = 1; + for (i = 0; i < numProbes; ++i) + { + finalContribColor += contribution[i] *probeContribColors[i].rgb; + contribAlpha -= contribution[i]; + } + + //Skylight coloration for anything not covered by probes above + finalContribColor += vec3(0.3, 0.3, 0.3) * contribAlpha; + + OUT_col = vec4(finalContribColor, 1); + return; +#endif + } + + vec3 irradiance = vec3(0, 0, 0); + vec3 specular = vec3(0, 0, 0); + + // Radiance (Specular) +#if DEBUGVIZ_SPECCUBEMAP == 0 + float lod = surface.roughness*cubeMips; +#elif DEBUGVIZ_SPECCUBEMAP == 1 + float lod = 0; +#endif + + alpha = 1; + for (i = 0; i < numProbes; ++i) + { + float contrib = contribution[i]; + if (contrib != 0) + { + float cubemapIdx = probeConfigData[i].a; + vec3 dir = boxProject(surface.P, surface.R, worldToObjArray[i], bbMinArray[i].xyz, bbMaxArray[i].xyz, inRefPosArray[i].xyz); + + //irradiance += textureLod(irradianceCubemapAR, vec4(dir, cubemapIdx), 0).xyz * contrib; + //specular += textureLod(specularCubemapAR, vec4(dir, cubemapIdx), lod).xyz * contrib; + irradiance += vec3(1,1,1) * contrib; + specular += vec3(1,1,1) * contrib; + alpha -= contrib; + } + } + + if (hasSkylight == 1 && alpha > 0.001) + { + irradiance += textureLod(skylightIrradMap, surface.R, 0).xyz * alpha; + specular += textureLod(skylightSpecularMap, surface.R, lod).xyz * alpha; + } + +#if DEBUGVIZ_SPECCUBEMAP == 1 && DEBUGVIZ_DIFFCUBEMAP == 0 + OUT_col = vec4(specular, 1); + return; +#elif DEBUGVIZ_DIFFCUBEMAP == 1 + OUT_col = vec4(irradiance, 1); + return; +#endif + + vec3 F = FresnelSchlickRoughness(surface.NdotV, surface.f0, surface.roughness); + + //energy conservation + vec3 kD = vec3(1,1,1) - F; + kD *= 1.0 - surface.metalness; + + //apply brdf + //Do it once to save on texture samples + vec2 brdf = texture(BRDFTexture, vec2(surface.roughness, surface.NdotV)).xy; + specular *= brdf.x * F + brdf.y; + + //final diffuse color + vec3 diffuse = kD * irradiance * surface.baseColor.rgb; + vec4 finalColor = vec4(diffuse + specular * surface.ao, 1.0); + + OUT_col = finalColor; +} diff --git a/gl/Timmy Note.txt b/gl/Timmy Note.txt new file mode 100644 index 000000000..2b023404e --- /dev/null +++ b/gl/Timmy Note.txt @@ -0,0 +1 @@ +idiot me used glTexImage2D for the cubemap array code, obviously should be glTexImage3D \ No newline at end of file diff --git a/gl/gfxGLCubemap.cpp b/gl/gfxGLCubemap.cpp new file mode 100644 index 000000000..493a508c7 --- /dev/null +++ b/gl/gfxGLCubemap.cpp @@ -0,0 +1,389 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gl/gfxGLDevice.h" +#include "gfx/gl/gfxGLTextureObject.h" +#include "gfx/gl/gfxGLEnumTranslate.h" +#include "gfx/gl/gfxGLUtils.h" +#include "gfx/gl/gfxGLCubemap.h" +#include "gfx/gfxTextureManager.h" +#include "gfx/gfxCardProfile.h" +#include "gfx/bitmap/ddsFile.h" +#include "gfx/bitmap/imageUtils.h" + + +static GLenum faceList[6] = +{ + GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z +}; + +GFXGLCubemap::GFXGLCubemap() : + mCubemap(0), + mDynamicTexSize(0), + mFaceFormat( GFXFormatR8G8B8A8 ) +{ + for(U32 i = 0; i < 6; i++) + mTextures[i] = NULL; + + GFXTextureManager::addEventDelegate( this, &GFXGLCubemap::_onTextureEvent ); +} + +GFXGLCubemap::~GFXGLCubemap() +{ + glDeleteTextures(1, &mCubemap); + GFXTextureManager::removeEventDelegate( this, &GFXGLCubemap::_onTextureEvent ); +} + +GLenum GFXGLCubemap::getEnumForFaceNumber(U32 face) +{ + return faceList[face]; +} + +void GFXGLCubemap::fillCubeTextures(GFXTexHandle* faces) +{ + AssertFatal( faces, ""); + AssertFatal( faces[0]->mMipLevels > 0, ""); + + PRESERVE_CUBEMAP_TEXTURE(); + glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemap); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, faces[0]->mMipLevels - 1 ); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + U32 reqWidth = faces[0]->getWidth(); + U32 reqHeight = faces[0]->getHeight(); + GFXFormat regFaceFormat = faces[0]->getFormat(); + const bool isCompressed = ImageUtil::isCompressedFormat(regFaceFormat); + mWidth = reqWidth; + mHeight = reqHeight; + mFaceFormat = regFaceFormat; + mMipMapLevels = getMax( (U32)1, faces[0]->mMipLevels); + AssertFatal(reqWidth == reqHeight, "GFXGLCubemap::fillCubeTextures - Width and height must be equal!"); + + for(U32 i = 0; i < 6; i++) + { + AssertFatal(faces[i], avar("GFXGLCubemap::fillCubeFaces - texture %i is NULL!", i)); + AssertFatal((faces[i]->getWidth() == reqWidth) && (faces[i]->getHeight() == reqHeight), "GFXGLCubemap::fillCubeFaces - All textures must have identical dimensions!"); + AssertFatal(faces[i]->getFormat() == regFaceFormat, "GFXGLCubemap::fillCubeFaces - All textures must have identical formats!"); + + mTextures[i] = faces[i]; + GFXFormat faceFormat = faces[i]->getFormat(); + + GFXGLTextureObject* glTex = static_cast(faces[i].getPointer()); + if( isCompressed ) + { + for( U32 mip = 0; mip < mMipMapLevels; ++mip ) + { + const U32 mipWidth = getMax( U32(1), faces[i]->getWidth() >> mip ); + const U32 mipHeight = getMax( U32(1), faces[i]->getHeight() >> mip ); + const U32 mipDataSize = getCompressedSurfaceSize( mFaceFormat, mWidth, mHeight, mip ); + + U8* buf = glTex->getTextureData( mip ); + glCompressedTexImage2D(faceList[i], mip, GFXGLTextureInternalFormat[mFaceFormat], mipWidth, mipHeight, 0, mipDataSize, buf); + delete[] buf; + } + } + else + { + U8* buf = glTex->getTextureData(); + glTexImage2D(faceList[i], 0, GFXGLTextureInternalFormat[faceFormat], mWidth, mHeight, + 0, GFXGLTextureFormat[faceFormat], GFXGLTextureType[faceFormat], buf); + delete[] buf; + } + } + + if( !isCompressed ) + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); +} + +void GFXGLCubemap::initStatic(GFXTexHandle* faces) +{ + if(mCubemap) + return; + + if(faces) + { + AssertFatal(faces[0], "GFXGLCubemap::initStatic - empty texture passed"); + glGenTextures(1, &mCubemap); + fillCubeTextures(faces); + } +} + +void GFXGLCubemap::initStatic( DDSFile *dds ) +{ + if(mCubemap) + return; + + AssertFatal( dds, "GFXGLCubemap::initStatic - Got null DDS file!" ); + AssertFatal( dds->isCubemap(), "GFXGLCubemap::initStatic - Got non-cubemap DDS file!" ); + AssertFatal( dds->mSurfaces.size() == 6, "GFXGLCubemap::initStatic - DDS has less than 6 surfaces!" ); + + mWidth = dds->getWidth(); + mHeight = dds->getHeight(); + mFaceFormat = dds->getFormat(); + mMipMapLevels = dds->getMipLevels(); + const bool isCompressed = ImageUtil::isCompressedFormat(mFaceFormat); + glGenTextures(1, &mCubemap); + + PRESERVE_CUBEMAP_TEXTURE(); + glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemap); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, mMipMapLevels - 1); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + AssertFatal(mWidth == mHeight, "GFXGLCubemap::initStatic - Width and height must be equal!"); + + for(U32 i = 0; i < 6; i++) + { + if ( !dds->mSurfaces[i] ) + { + // TODO: The DDS can skip surfaces, but i'm unsure what i should + // do here when creating the cubemap. Ignore it for now. + continue; + } + + // convert to Z up + const U32 faceIndex = _zUpFaceIndex(i); + + // Now loop thru the mip levels! + for (U32 mip = 0; mip < mMipMapLevels; ++mip) + { + const U32 mipWidth = getMax( U32(1), mWidth >> mip ); + const U32 mipHeight = getMax( U32(1), mHeight >> mip ); + if (isCompressed) + glCompressedTexImage2D(faceList[faceIndex], mip, GFXGLTextureInternalFormat[mFaceFormat], mipWidth, mipHeight, 0, dds->getSurfaceSize(mip), dds->mSurfaces[i]->mMips[mip]); + else + glTexImage2D(faceList[faceIndex], mip, GFXGLTextureInternalFormat[mFaceFormat], mipWidth, mipHeight, 0, + GFXGLTextureFormat[mFaceFormat], GFXGLTextureType[mFaceFormat], dds->mSurfaces[i]->mMips[mip]); + } + } +} + +void GFXGLCubemap::initDynamic(U32 texSize, GFXFormat faceFormat, U32 mipLevels) +{ + mDynamicTexSize = texSize; + mFaceFormat = faceFormat; + const bool isCompressed = ImageUtil::isCompressedFormat(faceFormat); + mMipMapLevels = getMax( (U32)1, getMaxMipmaps( texSize, texSize, 1 ) ); + + glGenTextures(1, &mCubemap); + PRESERVE_CUBEMAP_TEXTURE(); + glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemap); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, mMipMapLevels - 1); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + mWidth = texSize; + mHeight = texSize; + + for(U32 i = 0; i < 6; i++) + { + if( ImageUtil::isCompressedFormat(faceFormat) ) + { + for( U32 mip = 0; mip < mMipMapLevels; ++mip ) + { + const U32 mipSize = getMax( U32(1), texSize >> mip ); + const U32 mipDataSize = getCompressedSurfaceSize( mFaceFormat, texSize, texSize, mip ); + glCompressedTexImage2D(faceList[i], mip, GFXGLTextureInternalFormat[mFaceFormat], mipSize, mipSize, 0, mipDataSize, NULL); + } + } + else + { + glTexImage2D( faceList[i], 0, GFXGLTextureInternalFormat[faceFormat], texSize, texSize, + 0, GFXGLTextureFormat[faceFormat], GFXGLTextureType[faceFormat], NULL); + } + } + + if( !isCompressed ) + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); +} + +void GFXGLCubemap::zombify() +{ + glDeleteTextures(1, &mCubemap); + mCubemap = 0; +} + +void GFXGLCubemap::resurrect() +{ + // Handled in tmResurrect +} + +void GFXGLCubemap::tmResurrect() +{ + if(mDynamicTexSize) + initDynamic(mDynamicTexSize,mFaceFormat); + else + { + if ( mDDSFile ) + initStatic( mDDSFile ); + else + initStatic( mTextures ); + } +} + +void GFXGLCubemap::setToTexUnit(U32 tuNum) +{ + static_cast(getOwningDevice())->setCubemapInternal(tuNum, this); +} + +void GFXGLCubemap::bind(U32 textureUnit) const +{ + glActiveTexture(GL_TEXTURE0 + textureUnit); + glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemap); + static_cast(getOwningDevice())->getOpenglCache()->setCacheBindedTex(textureUnit, GL_TEXTURE_CUBE_MAP, mCubemap); + + GFXGLStateBlockRef sb = static_cast(GFX)->getCurrentStateBlock(); + AssertFatal(sb, "GFXGLCubemap::bind - No active stateblock!"); + if (!sb) + return; + + const GFXSamplerStateDesc& ssd = sb->getDesc().samplers[textureUnit]; + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, minificationFilter(ssd.minFilter, ssd.mipFilter, 0)); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GFXGLTextureFilter[ssd.magFilter]); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GFXGLTextureAddress[ssd.addressModeU]); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GFXGLTextureAddress[ssd.addressModeV]); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GFXGLTextureAddress[ssd.addressModeW]); +} + +void GFXGLCubemap::_onTextureEvent( GFXTexCallbackCode code ) +{ + if ( code == GFXZombify ) + zombify(); + else + tmResurrect(); +} + +U8* GFXGLCubemap::getTextureData(U32 face, U32 mip) +{ + AssertFatal(mMipMapLevels, ""); + mip = (mip < mMipMapLevels) ? mip : 0; + const U32 bytesPerTexel = 4; //TODO make work with more formats!!!!! + const U32 dataSize = ImageUtil::isCompressedFormat(mFaceFormat) + ? getCompressedSurfaceSize(mFaceFormat, mWidth, mHeight, mip) + : (mWidth >> mip) * (mHeight >> mip) * bytesPerTexel; + + U8* data = new U8[dataSize]; + PRESERVE_TEXTURE(GL_TEXTURE_CUBE_MAP); + glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemap); + + if (ImageUtil::isCompressedFormat(mFaceFormat)) + glGetCompressedTexImage(faceList[face], mip, data); + else + glGetTexImage(faceList[face], mip, GFXGLTextureFormat[mFaceFormat], GFXGLTextureType[mFaceFormat], data); + + return data; +} + +//----------------------------------------------------------------------------- +// Cubemap Array +//----------------------------------------------------------------------------- + +GFXGLCubemapArray::GFXGLCubemapArray() +{ +} + +GFXGLCubemapArray::~GFXGLCubemapArray() +{ + glDeleteTextures(1, &mCubemap); +} + +void GFXGLCubemapArray::initStatic(GFXCubemapHandle *cubemaps, const U32 cubemapCount) +{ + AssertFatal(cubemaps, "GFXGLCubemapArray- Got null GFXCubemapHandle!"); + AssertFatal(*cubemaps, "GFXGLCubemapArray - Got empty cubemap!"); + + //all cubemaps must be the same size,format and number of mipmaps. Grab the details from the first cubemap + mSize = cubemaps[0]->getSize(); + mFormat = cubemaps[0]->getFormat(); + mMipMapLevels = cubemaps[0]->getMipMapLevels(); + mNumCubemaps = cubemapCount; + const bool isCompressed = ImageUtil::isCompressedFormat(mFormat); + + glGenTextures(1, &mCubemap); + PRESERVE_CUBEMAP_ARRAY_TEXTURE(); + glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, mCubemap); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAX_LEVEL, mMipMapLevels - 1); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + for (U32 i = 0; i < cubemapCount; i++) + { + GFXGLCubemap* glTex = static_cast(cubemaps[i].getPointer()); + for (U32 face = 0; face < 6; face++) + { + for (U32 currentMip = 0; currentMip < mMipMapLevels; currentMip++) + { + U8 *pixelData = glTex->getTextureData(face, currentMip); + const U32 mipSize = getMax(U32(1), mSize >> currentMip); + if (isCompressed) + { + const U32 mipDataSize = getCompressedSurfaceSize(mFormat, mSize, mSize, currentMip); + glCompressedTexImage2D(faceList[face], currentMip, GFXGLTextureInternalFormat[mFormat], mipSize, mipSize, 0, mipDataSize, pixelData); + } + else + { + glTexImage2D(faceList[face], currentMip, GFXGLTextureInternalFormat[mFormat], mipSize, mipSize, + 0, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], pixelData); + } + delete[] pixelData; + } + } + } +} + +void GFXGLCubemapArray::setToTexUnit(U32 tuNum) +{ + static_cast(getOwningDevice())->setCubemapArrayInternal(tuNum, this); +} + +void GFXGLCubemapArray::bind(U32 textureUnit) const +{ + glActiveTexture(GL_TEXTURE0 + textureUnit); + glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, mCubemap); + static_cast(getOwningDevice())->getOpenglCache()->setCacheBindedTex(textureUnit, GL_TEXTURE_CUBE_MAP_ARRAY, mCubemap); + + GFXGLStateBlockRef sb = static_cast(GFX)->getCurrentStateBlock(); + AssertFatal(sb, "GFXGLCubemap::bind - No active stateblock!"); + if (!sb) + return; + + const GFXSamplerStateDesc& ssd = sb->getDesc().samplers[textureUnit]; + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, minificationFilter(ssd.minFilter, ssd.mipFilter, 0)); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GFXGLTextureFilter[ssd.magFilter]); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S, GFXGLTextureAddress[ssd.addressModeU]); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T, GFXGLTextureAddress[ssd.addressModeV]); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_R, GFXGLTextureAddress[ssd.addressModeW]); +} diff --git a/gl/gfxGLCubemap.h b/gl/gfxGLCubemap.h new file mode 100644 index 000000000..6fa7f0cd3 --- /dev/null +++ b/gl/gfxGLCubemap.h @@ -0,0 +1,113 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXGLCUBEMAP_H_ +#define _GFXGLCUBEMAP_H_ + +#ifndef _GFXCUBEMAP_H_ +#include "gfx/gfxCubemap.h" +#endif +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif + + +class GFXGLCubemap : public GFXCubemap +{ +public: + GFXGLCubemap(); + virtual ~GFXGLCubemap(); + + virtual void initStatic( GFXTexHandle *faces ); + virtual void initStatic( DDSFile *dds ); + virtual void initDynamic( U32 texSize, GFXFormat faceFormat = GFXFormatR8G8B8A8, U32 mipLevels = 0); + virtual U32 getSize() const { return mWidth; } + virtual GFXFormat getFormat() const { return mFaceFormat; } + + // Convenience methods for GFXGLTextureTarget + U32 getWidth() { return mWidth; } + U32 getHeight() { return mHeight; } + U32 getHandle() { return mCubemap; } + + // GFXResource interface + virtual void zombify(); + virtual void resurrect(); + + /// Called by texCB; this is to ensure that all textures have been resurrected before we attempt to res the cubemap. + void tmResurrect(); + + static GLenum getEnumForFaceNumber(U32 face);///< Performs lookup to get a GLenum for the given face number + + /// @return An array containing the texture data + /// @note You are responsible for deleting the returned data! (Use delete[]) + U8* getTextureData(U32 face, U32 mip = 0); + +protected: + + friend class GFXDevice; + friend class GFXGLDevice; + + /// The callback used to get texture events. + /// @see GFXTextureManager::addEventDelegate + void _onTextureEvent( GFXTexCallbackCode code ); + + GLuint mCubemap; ///< Internal GL handle + U32 mDynamicTexSize; ///< Size of faces for a dynamic texture (used in resurrect) + + // Self explanatory + U32 mWidth; + U32 mHeight; + GFXFormat mFaceFormat; + + GFXTexHandle mTextures[6]; ///< Keep refs to our textures for resurrection of static cubemaps + + /// The backing DDSFile uses to restore the faces + /// when the surface is lost. + Resource mDDSFile; + + // should only be called by GFXDevice + virtual void setToTexUnit( U32 tuNum ); ///< Binds the cubemap to the given texture unit + virtual void bind(U32 textureUnit) const; ///< Notifies our owning device that we want to be set to the given texture unit (used for GL internal state tracking) + void fillCubeTextures(GFXTexHandle* faces); ///< Copies the textures in faces into the cubemap + +}; + +class GFXGLCubemapArray : public GFXCubemapArray +{ +public: + GFXGLCubemapArray(); + virtual ~GFXGLCubemapArray(); + virtual void initStatic(GFXCubemapHandle *cubemaps, const U32 cubemapCount); + virtual void setToTexUnit(U32 tuNum); + + // GFXResource interface + virtual void zombify() {} + virtual void resurrect() {} + +protected: + friend class GFXGLDevice; + void bind(U32 textureUnit) const; + GLuint mCubemap; ///< Internal GL handle + +}; + +#endif diff --git a/gl/gfxGLDevice.cpp b/gl/gfxGLDevice.cpp new file mode 100644 index 000000000..7c968dd57 --- /dev/null +++ b/gl/gfxGLDevice.cpp @@ -0,0 +1,1029 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gl/gfxGLDevice.h" +#include "platform/platformGL.h" + +#include "gfx/gfxCubemap.h" +#include "gfx/screenshot.h" +#include "gfx/gfxDrawUtil.h" + +#include "gfx/gl/gfxGLEnumTranslate.h" +#include "gfx/gl/gfxGLVertexBuffer.h" +#include "gfx/gl/gfxGLPrimitiveBuffer.h" +#include "gfx/gl/gfxGLTextureTarget.h" +#include "gfx/gl/gfxGLTextureManager.h" +#include "gfx/gl/gfxGLTextureObject.h" +#include "gfx/gl/gfxGLCubemap.h" +#include "gfx/gl/gfxGLCardProfiler.h" +#include "gfx/gl/gfxGLWindowTarget.h" +#include "platform/platformDlibrary.h" +#include "gfx/gl/gfxGLShader.h" +#include "gfx/primBuilder.h" +#include "console/console.h" +#include "gfx/gl/gfxGLOcclusionQuery.h" +#include "materials/shaderData.h" +#include "gfx/gl/gfxGLStateCache.h" +#include "gfx/gl/gfxGLVertexAttribLocation.h" +#include "gfx/gl/gfxGLVertexDecl.h" +#include "shaderGen/shaderGen.h" + +GFXAdapter::CreateDeviceInstanceDelegate GFXGLDevice::mCreateDeviceInstance(GFXGLDevice::createInstance); + +GFXDevice *GFXGLDevice::createInstance( U32 adapterIndex ) +{ + return new GFXGLDevice(adapterIndex); +} + +namespace GL +{ + extern void gglPerformBinds(); + extern void gglPerformExtensionBinds(void *context); +} + +void loadGLCore() +{ + static bool coreLoaded = false; // Guess what this is for. + if(coreLoaded) + return; + coreLoaded = true; + + // Make sure we've got our GL bindings. + GL::gglPerformBinds(); +} + +void loadGLExtensions(void *context) +{ + static bool extensionsLoaded = false; + if(extensionsLoaded) + return; + extensionsLoaded = true; + + GL::gglPerformExtensionBinds(context); +} + +void STDCALL glDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, + const GLchar *message, const void *userParam) +{ + // JTH [11/24/2016]: This is a temporary fix so that we do not get spammed for redundant fbo changes. + // This only happens on Intel cards. This should be looked into sometime in the near future. + if (dStrStartsWith(message, "API_ID_REDUNDANT_FBO")) + return; + if (severity == GL_DEBUG_SEVERITY_HIGH) + Con::errorf("OPENGL: %s", message); + else if (severity == GL_DEBUG_SEVERITY_MEDIUM) + Con::warnf("OPENGL: %s", message); + else if (severity == GL_DEBUG_SEVERITY_LOW) + Con::printf("OPENGL: %s", message); +} + +void STDCALL glAmdDebugCallback(GLuint id, GLenum category, GLenum severity, GLsizei length, + const GLchar* message, GLvoid* userParam) +{ + if (severity == GL_DEBUG_SEVERITY_HIGH) + Con::errorf("AMDOPENGL: %s", message); + else if (severity == GL_DEBUG_SEVERITY_MEDIUM) + Con::warnf("AMDOPENGL: %s", message); + else if (severity == GL_DEBUG_SEVERITY_LOW) + Con::printf("AMDOPENGL: %s", message); +} + +void GFXGLDevice::initGLState() +{ + // We don't currently need to sync device state with a known good place because we are + // going to set everything in GFXGLStateBlock, but if we change our GFXGLStateBlock strategy, this may + // need to happen. + + // Deal with the card profiler here when we know we have a valid context. + mCardProfiler = new GFXGLCardProfiler(); + mCardProfiler->init(); + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, (GLint*)&mMaxShaderTextures); + // JTH: Needs removed, ffp + //glGetIntegerv(GL_MAX_TEXTURE_UNITS, (GLint*)&mMaxFFTextures); + glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, (GLint*)&mMaxTRColors); + mMaxTRColors = getMin( mMaxTRColors, (U32)(GFXTextureTarget::MaxRenderSlotId-1) ); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + // [JTH 5/6/2016] GLSL 1.50 is really SM 4.0 + // Setting mPixelShaderVersion to 3.0 will allow Advanced Lighting to run. + mPixelShaderVersion = 3.0; + + // Set capability extensions. + mCapabilities.anisotropicFiltering = mCardProfiler->queryProfile("GL_EXT_texture_filter_anisotropic"); + mCapabilities.bufferStorage = mCardProfiler->queryProfile("GL_ARB_buffer_storage"); + mCapabilities.shaderModel5 = mCardProfiler->queryProfile("GL_ARB_gpu_shader5"); + mCapabilities.textureStorage = mCardProfiler->queryProfile("GL_ARB_texture_storage"); + mCapabilities.samplerObjects = mCardProfiler->queryProfile("GL_ARB_sampler_objects"); + mCapabilities.copyImage = mCardProfiler->queryProfile("GL_ARB_copy_image"); + mCapabilities.vertexAttributeBinding = mCardProfiler->queryProfile("GL_ARB_vertex_attrib_binding"); + + String vendorStr = (const char*)glGetString( GL_VENDOR ); + if( vendorStr.find("NVIDIA", 0, String::NoCase | String::Left) != String::NPos) + mUseGlMap = false; + + // Workaround for all Mac's, has a problem using glMap* with volatile buffers +#ifdef TORQUE_OS_MAC + mUseGlMap = false; +#endif + +#if TORQUE_DEBUG + if( gglHasExtension(ARB_debug_output) ) + { + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallbackARB(glDebugCallback, NULL); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); + GLuint unusedIds = 0; + glDebugMessageControlARB(GL_DONT_CARE, + GL_DONT_CARE, + GL_DONT_CARE, + 0, + &unusedIds, + GL_TRUE); + } + else if(gglHasExtension(AMD_debug_output)) + { + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallbackAMD(glAmdDebugCallback, NULL); + //glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); + GLuint unusedIds = 0; + glDebugMessageEnableAMD(GL_DONT_CARE, GL_DONT_CARE, 0,&unusedIds, GL_TRUE); + } +#endif + + PlatformGL::setVSync(smDisableVSync ? 0 : 1); + + //install vsync callback + Con::NotifyDelegate clbk( this, &GFXGLDevice::vsyncCallback ); + Con::addVariableNotify( "$pref::Video::disableVerticalSync", clbk ); + + //OpenGL 3 need a binded VAO for render + GLuint vao; + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + //enable sRGB + glEnable(GL_FRAMEBUFFER_SRGB); +} + +void GFXGLDevice::vsyncCallback() +{ + PlatformGL::setVSync(smDisableVSync ? 0 : 1); +} + +GFXGLDevice::GFXGLDevice(U32 adapterIndex) : + mAdapterIndex(adapterIndex), + mNeedUpdateVertexAttrib(false), + mCurrentPB(NULL), + mDrawInstancesCount(0), + mCurrentShader( NULL ), + m_mCurrentWorld(true), + m_mCurrentView(true), + mContext(NULL), + mPixelFormat(NULL), + mPixelShaderVersion(0.0f), + mMaxShaderTextures(2), + mMaxFFTextures(2), + mMaxTRColors(1), + mClip(0, 0, 0, 0), + mWindowRT(NULL), + mUseGlMap(true) +{ + for(int i = 0; i < VERTEX_STREAM_COUNT; ++i) + { + mCurrentVB[i] = NULL; + mCurrentVB_Divisor[i] = 0; + } + + // Initiailize capabilities to false. + memset(&mCapabilities, 0, sizeof(GLCapabilities)); + + loadGLCore(); + + GFXGLEnumTranslate::init(); + + GFXVertexColor::setSwizzle( &Swizzles::rgba ); + + // OpenGL have native RGB, no need swizzle + mDeviceSwizzle32 = &Swizzles::rgba; + mDeviceSwizzle24 = &Swizzles::rgb; + + mTextureManager = new GFXGLTextureManager(); + gScreenShot = new ScreenShot(); + + for(U32 i = 0; i < TEXTURE_STAGE_COUNT; i++) + mActiveTextureType[i] = GL_ZERO; + + mNumVertexStream = 2; + + for(int i = 0; i < GS_COUNT; ++i) + mModelViewProjSC[i] = NULL; + + mOpenglStateCache = new GFXGLStateCache; + +} + +GFXGLDevice::~GFXGLDevice() +{ + mCurrentStateBlock = NULL; + + for(int i = 0; i < VERTEX_STREAM_COUNT; ++i) + mCurrentVB[i] = NULL; + mCurrentPB = NULL; + + for(U32 i = 0; i < mVolatileVBs.size(); i++) + mVolatileVBs[i] = NULL; + for(U32 i = 0; i < mVolatilePBs.size(); i++) + mVolatilePBs[i] = NULL; + + // Clear out our current texture references + for (U32 i = 0; i < TEXTURE_STAGE_COUNT; i++) + { + mCurrentTexture[i] = NULL; + mNewTexture[i] = NULL; + mCurrentCubemap[i] = NULL; + mNewCubemap[i] = NULL; + } + + mRTStack.clear(); + mCurrentRT = NULL; + + if( mTextureManager ) + { + mTextureManager->zombify(); + mTextureManager->kill(); + } + + GFXResource* walk = mResourceListHead; + while(walk) + { + walk->zombify(); + walk = walk->getNextResource(); + } + + if( mCardProfiler ) + SAFE_DELETE( mCardProfiler ); + + SAFE_DELETE( gScreenShot ); + + SAFE_DELETE( mOpenglStateCache ); +} + +void GFXGLDevice::zombify() +{ + mTextureManager->zombify(); + + for(int i = 0; i < VERTEX_STREAM_COUNT; ++i) + if(mCurrentVB[i]) + mCurrentVB[i]->finish(); + if(mCurrentPB) + mCurrentPB->finish(); + + //mVolatileVBs.clear(); + //mVolatilePBs.clear(); + GFXResource* walk = mResourceListHead; + while(walk) + { + walk->zombify(); + walk = walk->getNextResource(); + } +} + +void GFXGLDevice::resurrect() +{ + GFXResource* walk = mResourceListHead; + while(walk) + { + walk->resurrect(); + walk = walk->getNextResource(); + } + for(int i = 0; i < VERTEX_STREAM_COUNT; ++i) + if(mCurrentVB[i]) + mCurrentVB[i]->prepare(); + if(mCurrentPB) + mCurrentPB->prepare(); + + mTextureManager->resurrect(); +} + +GFXVertexBuffer* GFXGLDevice::findVolatileVBO(U32 numVerts, const GFXVertexFormat *vertexFormat, U32 vertSize) +{ + PROFILE_SCOPE(GFXGLDevice_findVBPool); + for(U32 i = 0; i < mVolatileVBs.size(); i++) + if ( mVolatileVBs[i]->mNumVerts >= numVerts && + mVolatileVBs[i]->mVertexFormat.isEqual( *vertexFormat ) && + mVolatileVBs[i]->mVertexSize == vertSize && + mVolatileVBs[i]->getRefCount() == 1 ) + return mVolatileVBs[i]; + + // No existing VB, so create one + PROFILE_SCOPE(GFXGLDevice_createVBPool); + StrongRefPtr buf(new GFXGLVertexBuffer(GFX, numVerts, vertexFormat, vertSize, GFXBufferTypeVolatile)); + buf->registerResourceWithDevice(this); + mVolatileVBs.push_back(buf); + return buf.getPointer(); +} + +GFXPrimitiveBuffer* GFXGLDevice::findVolatilePBO(U32 numIndices, U32 numPrimitives) +{ + for(U32 i = 0; i < mVolatilePBs.size(); i++) + if((mVolatilePBs[i]->mIndexCount >= numIndices) && (mVolatilePBs[i]->getRefCount() == 1)) + return mVolatilePBs[i]; + + // No existing PB, so create one + StrongRefPtr buf(new GFXGLPrimitiveBuffer(GFX, numIndices, numPrimitives, GFXBufferTypeVolatile)); + buf->registerResourceWithDevice(this); + mVolatilePBs.push_back(buf); + return buf.getPointer(); +} + +GFXVertexBuffer *GFXGLDevice::allocVertexBuffer( U32 numVerts, + const GFXVertexFormat *vertexFormat, + U32 vertSize, + GFXBufferType bufferType, + void* data ) +{ + PROFILE_SCOPE(GFXGLDevice_allocVertexBuffer); + if(bufferType == GFXBufferTypeVolatile) + return findVolatileVBO(numVerts, vertexFormat, vertSize); + + GFXGLVertexBuffer* buf = new GFXGLVertexBuffer( GFX, numVerts, vertexFormat, vertSize, bufferType ); + buf->registerResourceWithDevice(this); + + if(data) + { + void* dest; + buf->lock(0, numVerts, &dest); + dMemcpy(dest, data, vertSize * numVerts); + buf->unlock(); + } + + return buf; +} + +GFXPrimitiveBuffer *GFXGLDevice::allocPrimitiveBuffer( U32 numIndices, U32 numPrimitives, GFXBufferType bufferType, void* data ) +{ + GFXPrimitiveBuffer* buf; + + if(bufferType == GFXBufferTypeVolatile) + { + buf = findVolatilePBO(numIndices, numPrimitives); + } + else + { + buf = new GFXGLPrimitiveBuffer(GFX, numIndices, numPrimitives, bufferType); + buf->registerResourceWithDevice(this); + } + + if(data) + { + void* dest; + buf->lock(0, numIndices, &dest); + dMemcpy(dest, data, sizeof(U16) * numIndices); + buf->unlock(); + } + return buf; +} + +void GFXGLDevice::setVertexStream( U32 stream, GFXVertexBuffer *buffer ) +{ + AssertFatal(stream <= 1, "GFXGLDevice::setVertexStream only support 2 stream (0: data, 1: instancing)"); + + //if(mCurrentVB[stream] != buffer) + { + // Reset the state the old VB required, then set the state the new VB requires. + if( mCurrentVB[stream] ) + { + mCurrentVB[stream]->finish(); + } + + mCurrentVB[stream] = static_cast( buffer ); + + mNeedUpdateVertexAttrib = true; + } +} + +void GFXGLDevice::setVertexStreamFrequency( U32 stream, U32 frequency ) +{ + if( stream == 0 ) + { + mCurrentVB_Divisor[stream] = 0; // non instanced, is vertex buffer + mDrawInstancesCount = frequency; // instances count + } + else + { + AssertFatal(frequency <= 1, "GFXGLDevice::setVertexStreamFrequency only support 0/1 for this stream" ); + if( stream == 1 && frequency == 1 ) + mCurrentVB_Divisor[stream] = 1; // instances data need a frequency of 1 + else + mCurrentVB_Divisor[stream] = 0; + } + + mNeedUpdateVertexAttrib = true; +} + +GFXCubemap* GFXGLDevice::createCubemap() +{ + GFXGLCubemap* cube = new GFXGLCubemap(); + cube->registerResourceWithDevice(this); + return cube; +}; + +GFXCubemapArray *GFXGLDevice::createCubemapArray() +{ + GFXGLCubemapArray* cubeArray = new GFXGLCubemapArray(); + cubeArray->registerResourceWithDevice(this); + return cubeArray; +} + +void GFXGLDevice::endSceneInternal() +{ + // nothing to do for opengl + mCanCurrentlyRender = false; +} + +void GFXGLDevice::clear(U32 flags, const LinearColorF& color, F32 z, U32 stencil) +{ + // Make sure we have flushed our render target state. + _updateRenderTargets(); + + bool writeAllColors = true; + bool zwrite = true; + bool writeAllStencil = true; + const GFXStateBlockDesc *desc = NULL; + if (mCurrentGLStateBlock) + { + desc = &mCurrentGLStateBlock->getDesc(); + zwrite = desc->zWriteEnable; + writeAllColors = desc->colorWriteRed && desc->colorWriteGreen && desc->colorWriteBlue && desc->colorWriteAlpha; + writeAllStencil = desc->stencilWriteMask == 0xFFFFFFFF; + } + + glColorMask(true, true, true, true); + glDepthMask(true); + glStencilMask(0xFFFFFFFF); + glClearColor(color.red, color.green, color.blue, color.alpha); + glClearDepth(z); + glClearStencil(stencil); + + GLbitfield clearflags = 0; + clearflags |= (flags & GFXClearTarget) ? GL_COLOR_BUFFER_BIT : 0; + clearflags |= (flags & GFXClearZBuffer) ? GL_DEPTH_BUFFER_BIT : 0; + clearflags |= (flags & GFXClearStencil) ? GL_STENCIL_BUFFER_BIT : 0; + + glClear(clearflags); + + if(!writeAllColors) + glColorMask(desc->colorWriteRed, desc->colorWriteGreen, desc->colorWriteBlue, desc->colorWriteAlpha); + + if(!zwrite) + glDepthMask(false); + + if(!writeAllStencil) + glStencilMask(desc->stencilWriteMask); +} + +void GFXGLDevice::clearColorAttachment(const U32 attachment, const LinearColorF& color) +{ + const GLfloat clearColor[4] = { color.red, color.green, color.blue, color.alpha }; + glClearBufferfv(GL_COLOR, attachment, clearColor); +} + +// Given a primitive type and a number of primitives, return the number of indexes/vertexes used. +inline GLsizei GFXGLDevice::primCountToIndexCount(GFXPrimitiveType primType, U32 primitiveCount) +{ + switch (primType) + { + case GFXPointList : + return primitiveCount; + break; + case GFXLineList : + return primitiveCount * 2; + break; + case GFXLineStrip : + return primitiveCount + 1; + break; + case GFXTriangleList : + return primitiveCount * 3; + break; + case GFXTriangleStrip : + return 2 + primitiveCount; + break; + default: + AssertFatal(false, "GFXGLDevice::primCountToIndexCount - unrecognized prim type"); + break; + } + + return 0; +} + +GFXVertexDecl* GFXGLDevice::allocVertexDecl( const GFXVertexFormat *vertexFormat ) +{ + PROFILE_SCOPE(GFXGLDevice_allocVertexDecl); + typedef Map GFXGLVertexDeclMap; + static GFXGLVertexDeclMap declMap; + GFXGLVertexDeclMap::Iterator itr = declMap.find( (void*)vertexFormat->getDescription().c_str() ); // description string are interned, safe to use c_str() + if(itr != declMap.end()) + return &itr->value; + + GFXGLVertexDecl &decl = declMap[(void*)vertexFormat->getDescription().c_str()]; + decl.init(vertexFormat); + return &decl; +} + +void GFXGLDevice::setVertexDecl( const GFXVertexDecl *decl ) +{ + static_cast(decl)->prepareVertexFormat(); +} + +inline void GFXGLDevice::preDrawPrimitive() +{ + if( mStateDirty ) + { + updateStates(); + } + + if(mCurrentShaderConstBuffer) + setShaderConstBufferInternal(mCurrentShaderConstBuffer); + + if( mNeedUpdateVertexAttrib ) + { + AssertFatal(mCurrVertexDecl, ""); + const GFXGLVertexDecl* decl = static_cast(mCurrVertexDecl); + + for(int i = 0; i < getNumVertexStreams(); ++i) + { + if(mCurrentVB[i]) + { + mCurrentVB[i]->prepare(i, mCurrentVB_Divisor[i]); // GL_ARB_vertex_attrib_binding + decl->prepareBuffer_old( i, mCurrentVB[i]->mBuffer, mCurrentVB_Divisor[i] ); // old vertex buffer/format + } + } + + decl->updateActiveVertexAttrib( GFXGL->getOpenglCache()->getCacheVertexAttribActive() ); + } + + mNeedUpdateVertexAttrib = false; +} + +inline void GFXGLDevice::postDrawPrimitive(U32 primitiveCount) +{ + mDeviceStatistics.mDrawCalls++; + mDeviceStatistics.mPolyCount += primitiveCount; +} + +void GFXGLDevice::drawPrimitive( GFXPrimitiveType primType, U32 vertexStart, U32 primitiveCount ) +{ + preDrawPrimitive(); + + if(mCurrentVB[0]) + vertexStart += mCurrentVB[0]->mBufferVertexOffset; + + if(mDrawInstancesCount) + glDrawArraysInstanced(GFXGLPrimType[primType], vertexStart, primCountToIndexCount(primType, primitiveCount), mDrawInstancesCount); + else + glDrawArrays(GFXGLPrimType[primType], vertexStart, primCountToIndexCount(primType, primitiveCount)); + + postDrawPrimitive(primitiveCount); +} + +void GFXGLDevice::drawIndexedPrimitive( GFXPrimitiveType primType, + U32 startVertex, + U32 minIndex, + U32 numVerts, + U32 startIndex, + U32 primitiveCount ) +{ + preDrawPrimitive(); + + U16* buf = (U16*)static_cast(mCurrentPrimitiveBuffer.getPointer())->getBuffer() + startIndex + mCurrentPrimitiveBuffer->mVolatileStart; + + const U32 baseVertex = mCurrentVB[0]->mBufferVertexOffset + startVertex; + + if(mDrawInstancesCount) + glDrawElementsInstancedBaseVertex(GFXGLPrimType[primType], primCountToIndexCount(primType, primitiveCount), GL_UNSIGNED_SHORT, buf, mDrawInstancesCount, baseVertex); + else + glDrawElementsBaseVertex(GFXGLPrimType[primType], primCountToIndexCount(primType, primitiveCount), GL_UNSIGNED_SHORT, buf, baseVertex); + + postDrawPrimitive(primitiveCount); +} + +void GFXGLDevice::setPB(GFXGLPrimitiveBuffer* pb) +{ + if(mCurrentPB) + mCurrentPB->finish(); + mCurrentPB = pb; +} + +void GFXGLDevice::setLightInternal(U32 lightStage, const GFXLightInfo light, bool lightEnable) +{ + // ONLY NEEDED ON FFP +} + +void GFXGLDevice::setLightMaterialInternal(const GFXLightMaterial mat) +{ + // ONLY NEEDED ON FFP +} + +void GFXGLDevice::setGlobalAmbientInternal(LinearColorF color) +{ + // ONLY NEEDED ON FFP +} + +void GFXGLDevice::setTextureInternal(U32 textureUnit, const GFXTextureObject*texture) +{ + GFXGLTextureObject *tex = static_cast(const_cast(texture)); + if (tex) + { + mActiveTextureType[textureUnit] = tex->getBinding(); + tex->bind(textureUnit); + } + else if(mActiveTextureType[textureUnit] != GL_ZERO) + { + glActiveTexture(GL_TEXTURE0 + textureUnit); + glBindTexture(mActiveTextureType[textureUnit], 0); + getOpenglCache()->setCacheBindedTex(textureUnit, mActiveTextureType[textureUnit], 0); + mActiveTextureType[textureUnit] = GL_ZERO; + } +} + +void GFXGLDevice::setCubemapInternal(U32 textureUnit, const GFXGLCubemap* texture) +{ + if(texture) + { + mActiveTextureType[textureUnit] = GL_TEXTURE_CUBE_MAP; + texture->bind(textureUnit); + } + else if(mActiveTextureType[textureUnit] != GL_ZERO) + { + glActiveTexture(GL_TEXTURE0 + textureUnit); + glBindTexture(mActiveTextureType[textureUnit], 0); + getOpenglCache()->setCacheBindedTex(textureUnit, mActiveTextureType[textureUnit], 0); + mActiveTextureType[textureUnit] = GL_ZERO; + } +} + +void GFXGLDevice::setCubemapArrayInternal(U32 textureUnit, const GFXGLCubemapArray* texture) +{ + if (texture) + { + mActiveTextureType[textureUnit] = GL_TEXTURE_CUBE_MAP_ARRAY; + texture->bind(textureUnit); + } + else if (mActiveTextureType[textureUnit] != GL_ZERO) + { + glActiveTexture(GL_TEXTURE0 + textureUnit); + glBindTexture(mActiveTextureType[textureUnit], 0); + getOpenglCache()->setCacheBindedTex(textureUnit, mActiveTextureType[textureUnit], 0); + mActiveTextureType[textureUnit] = GL_ZERO; + } +} + +void GFXGLDevice::setMatrix( GFXMatrixType mtype, const MatrixF &mat ) +{ + // ONLY NEEDED ON FFP +} + +void GFXGLDevice::setClipRect( const RectI &inRect ) +{ + AssertFatal(mCurrentRT.isValid(), "GFXGLDevice::setClipRect - must have a render target set to do any rendering operations!"); + + // Clip the rect against the renderable size. + Point2I size = mCurrentRT->getSize(); + RectI maxRect(Point2I(0,0), size); + mClip = inRect; + mClip.intersect(maxRect); + + // Create projection matrix. See http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/ortho.html + const F32 left = mClip.point.x; + const F32 right = mClip.point.x + mClip.extent.x; + const F32 bottom = mClip.extent.y; + const F32 top = 0.0f; + const F32 nearPlane = 0.0f; + const F32 farPlane = 1.0f; + + const F32 tx = -(right + left)/(right - left); + const F32 ty = -(top + bottom)/(top - bottom); + const F32 tz = -(farPlane + nearPlane)/(farPlane - nearPlane); + + static Point4F pt; + pt.set(2.0f / (right - left), 0.0f, 0.0f, 0.0f); + mProjectionMatrix.setColumn(0, pt); + + pt.set(0.0f, 2.0f/(top - bottom), 0.0f, 0.0f); + mProjectionMatrix.setColumn(1, pt); + + pt.set(0.0f, 0.0f, -2.0f/(farPlane - nearPlane), 0.0f); + mProjectionMatrix.setColumn(2, pt); + + pt.set(tx, ty, tz, 1.0f); + mProjectionMatrix.setColumn(3, pt); + + // Translate projection matrix. + static MatrixF translate(true); + pt.set(0.0f, -mClip.point.y, 0.0f, 1.0f); + translate.setColumn(3, pt); + + mProjectionMatrix *= translate; + + setMatrix(GFXMatrixProjection, mProjectionMatrix); + + MatrixF mTempMatrix(true); + setViewMatrix( mTempMatrix ); + setWorldMatrix( mTempMatrix ); + + // Set the viewport to the clip rect + RectI viewport(mClip.point.x, mClip.point.y, mClip.extent.x, mClip.extent.y); + setViewport(viewport); +} + +/// Creates a state block object based on the desc passed in. This object +/// represents an immutable state. +GFXStateBlockRef GFXGLDevice::createStateBlockInternal(const GFXStateBlockDesc& desc) +{ + return GFXStateBlockRef(new GFXGLStateBlock(desc)); +} + +/// Activates a stateblock +void GFXGLDevice::setStateBlockInternal(GFXStateBlock* block, bool force) +{ + AssertFatal(dynamic_cast(block), "GFXGLDevice::setStateBlockInternal - Incorrect stateblock type for this device!"); + GFXGLStateBlock* glBlock = static_cast(block); + GFXGLStateBlock* glCurrent = static_cast(mCurrentStateBlock.getPointer()); + if (force) + glCurrent = NULL; + + glBlock->activate(glCurrent); // Doesn't use current yet. + mCurrentGLStateBlock = glBlock; +} + +//------------------------------------------------------------------------------ + +GFXTextureTarget * GFXGLDevice::allocRenderToTextureTarget(bool genMips) +{ + GFXGLTextureTarget *targ = new GFXGLTextureTarget(genMips); + targ->registerResourceWithDevice(this); + return targ; +} + +GFXFence * GFXGLDevice::createFence() +{ + GFXFence* fence = _createPlatformSpecificFence(); + if(!fence) + fence = new GFXGeneralFence( this ); + + fence->registerResourceWithDevice(this); + return fence; +} + +GFXOcclusionQuery* GFXGLDevice::createOcclusionQuery() +{ + GFXOcclusionQuery *query = new GFXGLOcclusionQuery( this ); + query->registerResourceWithDevice(this); + return query; +} + +void GFXGLDevice::setupGenericShaders( GenericShaderType type ) +{ + AssertFatal(type != GSTargetRestore, ""); + + if( mGenericShader[GSColor] == NULL ) + { + ShaderData *shaderData; + + shaderData = new ShaderData(); + shaderData->setField("OGLVertexShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/gl/colorV.glsl")); + shaderData->setField("OGLPixelShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/gl/colorP.glsl")); + shaderData->setField("pixVersion", "2.0"); + shaderData->registerObject(); + mGenericShader[GSColor] = shaderData->getShader(); + mGenericShaderBuffer[GSColor] = mGenericShader[GSColor]->allocConstBuffer(); + mModelViewProjSC[GSColor] = mGenericShader[GSColor]->getShaderConstHandle( "$modelView" ); + Sim::getRootGroup()->addObject(shaderData); + + shaderData = new ShaderData(); + shaderData->setField("OGLVertexShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/gl/modColorTextureV.glsl")); + shaderData->setField("OGLPixelShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/gl/modColorTextureP.glsl")); + shaderData->setSamplerName("$diffuseMap", 0); + shaderData->setField("pixVersion", "2.0"); + shaderData->registerObject(); + mGenericShader[GSModColorTexture] = shaderData->getShader(); + mGenericShaderBuffer[GSModColorTexture] = mGenericShader[GSModColorTexture]->allocConstBuffer(); + mModelViewProjSC[GSModColorTexture] = mGenericShader[GSModColorTexture]->getShaderConstHandle( "$modelView" ); + Sim::getRootGroup()->addObject(shaderData); + + shaderData = new ShaderData(); + shaderData->setField("OGLVertexShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/gl/addColorTextureV.glsl")); + shaderData->setField("OGLPixelShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/gl/addColorTextureP.glsl")); + shaderData->setSamplerName("$diffuseMap", 0); + shaderData->setField("pixVersion", "2.0"); + shaderData->registerObject(); + mGenericShader[GSAddColorTexture] = shaderData->getShader(); + mGenericShaderBuffer[GSAddColorTexture] = mGenericShader[GSAddColorTexture]->allocConstBuffer(); + mModelViewProjSC[GSAddColorTexture] = mGenericShader[GSAddColorTexture]->getShaderConstHandle( "$modelView" ); + Sim::getRootGroup()->addObject(shaderData); + + shaderData = new ShaderData(); + shaderData->setField("OGLVertexShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/gl/textureV.glsl")); + shaderData->setField("OGLPixelShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/gl/textureP.glsl")); + shaderData->setSamplerName("$diffuseMap", 0); + shaderData->setField("pixVersion", "2.0"); + shaderData->registerObject(); + mGenericShader[GSTexture] = shaderData->getShader(); + mGenericShaderBuffer[GSTexture] = mGenericShader[GSTexture]->allocConstBuffer(); + mModelViewProjSC[GSTexture] = mGenericShader[GSTexture]->getShaderConstHandle( "$modelView" ); + Sim::getRootGroup()->addObject(shaderData); + } + + MatrixF tempMatrix = mProjectionMatrix * mViewMatrix * mWorldMatrix[mWorldStackSize]; + mGenericShaderBuffer[type]->setSafe(mModelViewProjSC[type], tempMatrix); + + setShader( mGenericShader[type] ); + setShaderConstBuffer( mGenericShaderBuffer[type] ); +} +GFXShader* GFXGLDevice::createShader() +{ + GFXGLShader* shader = new GFXGLShader(); + shader->registerResourceWithDevice( this ); + return shader; +} + +void GFXGLDevice::setShader(GFXShader *shader, bool force) +{ + if(mCurrentShader == shader && !force) + return; + + if ( shader ) + { + GFXGLShader *glShader = static_cast( shader ); + glShader->useProgram(); + mCurrentShader = shader; + } + else + { + setupGenericShaders(); + } +} + +void GFXGLDevice::setShaderConstBufferInternal(GFXShaderConstBuffer* buffer) +{ + PROFILE_SCOPE(GFXGLDevice_setShaderConstBufferInternal); + static_cast(buffer)->activate(); +} + +U32 GFXGLDevice::getNumSamplers() const +{ + return getMin((U32)TEXTURE_STAGE_COUNT,mPixelShaderVersion > 0.001f ? mMaxShaderTextures : mMaxFFTextures); +} + +GFXTextureObject* GFXGLDevice::getDefaultDepthTex() const +{ + if(mWindowRT && mWindowRT->getPointer()) + return static_cast( mWindowRT->getPointer() )->mBackBufferDepthTex.getPointer(); + + return NULL; +} + +U32 GFXGLDevice::getNumRenderTargets() const +{ + return mMaxTRColors; +} + +void GFXGLDevice::_updateRenderTargets() +{ + if ( mRTDirty || mCurrentRT->isPendingState() ) + { + if ( mRTDeactivate ) + { + mRTDeactivate->deactivate(); + mRTDeactivate = NULL; + } + + // NOTE: The render target changes is not really accurate + // as the GFXTextureTarget supports MRT internally. So when + // we activate a GFXTarget it could result in multiple calls + // to SetRenderTarget on the actual device. + mDeviceStatistics.mRenderTargetChanges++; + + GFXGLTextureTarget *tex = dynamic_cast( mCurrentRT.getPointer() ); + if ( tex ) + { + tex->applyState(); + tex->makeActive(); + } + else + { + GFXGLWindowTarget *win = dynamic_cast( mCurrentRT.getPointer() ); + AssertFatal( win != NULL, + "GFXGLDevice::_updateRenderTargets() - invalid target subclass passed!" ); + + win->makeActive(); + + if( win->mContext != static_cast(GFX)->mContext ) + { + mRTDirty = false; + GFX->updateStates(true); + } + } + + mRTDirty = false; + } + + if ( mViewportDirty ) + { + glViewport( mViewport.point.x, mViewport.point.y, mViewport.extent.x, mViewport.extent.y ); + mViewportDirty = false; + } +} + +GFXFormat GFXGLDevice::selectSupportedFormat( GFXTextureProfile* profile, + const Vector& formats, + bool texture, + bool mustblend, + bool mustfilter ) +{ + for(U32 i = 0; i < formats.size(); i++) + { + // Single channel textures are not supported by FBOs. + if(profile->testFlag(GFXTextureProfile::RenderTarget) && (formats[i] == GFXFormatA8 || formats[i] == GFXFormatL8 || formats[i] == GFXFormatL16)) + continue; + if(GFXGLTextureInternalFormat[formats[i]] == GL_ZERO) + continue; + + return formats[i]; + } + + return GFXFormatR8G8B8A8; +} + +U32 GFXGLDevice::getTotalVideoMemory_GL_EXT() +{ + // Source: http://www.opengl.org/registry/specs/ATI/meminfo.txt + if( gglHasExtension(ATI_meminfo) ) + { + GLint mem[4] = {0}; + glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, mem); // Retrieve the texture pool + + /* With mem[0] i get only the total memory free in the pool in KB + * + * mem[0] - total memory free in the pool + * mem[1] - largest available free block in the pool + * mem[2] - total auxiliary memory free + * mem[3] - largest auxiliary free block + */ + + return mem[0] / 1024; + } + + //source http://www.opengl.org/registry/specs/NVX/gpu_memory_info.txt + else if( gglHasExtension(NVX_gpu_memory_info) ) + { + GLint mem = 0; + glGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &mem); + return mem / 1024; + } + + // TODO OPENGL, add supprt for INTEL cards. + + return 0; +} + +// +// Register this device with GFXInit +// +class GFXGLRegisterDevice +{ +public: + GFXGLRegisterDevice() + { + GFXInit::getRegisterDeviceSignal().notify(&GFXGLDevice::enumerateAdapters); + } +}; + +static GFXGLRegisterDevice pGLRegisterDevice; + +ConsoleFunction(cycleResources, void, 1, 1, "") +{ + static_cast(GFX)->zombify(); + static_cast(GFX)->resurrect(); +} diff --git a/gl/gfxGLDevice.h b/gl/gfxGLDevice.h new file mode 100644 index 000000000..a93ae8a7b --- /dev/null +++ b/gl/gfxGLDevice.h @@ -0,0 +1,281 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXGLDEVICE_H_ +#define _GFXGLDEVICE_H_ + +#include "platform/platform.h" + +#include "gfx/gfxDevice.h" +#include "gfx/gfxInit.h" + +#include "gfx/gl/tGL/tGL.h" + +#include "windowManager/platformWindow.h" +#include "gfx/gfxFence.h" +#include "gfx/gfxResource.h" +#include "gfx/gl/gfxGLStateBlock.h" + +class GFXGLVertexBuffer; +class GFXGLPrimitiveBuffer; +class GFXGLTextureTarget; +class GFXGLCubemap; +class GFXGLCubemapArray; +class GFXGLStateCache; +class GFXGLVertexDecl; + +class GFXGLDevice : public GFXDevice +{ +public: + struct GLCapabilities + { + bool anisotropicFiltering; + bool bufferStorage; + bool shaderModel5; + bool textureStorage; + bool samplerObjects; + bool copyImage; + bool vertexAttributeBinding; + }; + GLCapabilities mCapabilities; + + void zombify(); + void resurrect(); + GFXGLDevice(U32 adapterIndex); + virtual ~GFXGLDevice(); + + static void enumerateAdapters( Vector &adapterList ); + static GFXDevice *createInstance( U32 adapterIndex ); + + virtual void init( const GFXVideoMode &mode, PlatformWindow *window = NULL ); + + virtual void activate() { } + virtual void deactivate() { } + virtual GFXAdapterType getAdapterType() { return OpenGL; } + + virtual void enterDebugEvent(ColorI color, const char *name); + virtual void leaveDebugEvent(); + virtual void setDebugMarker(ColorI color, const char *name); + + virtual void enumerateVideoModes(); + + virtual U32 getTotalVideoMemory_GL_EXT(); + virtual U32 getTotalVideoMemory(); + + virtual GFXCubemap * createCubemap(); + virtual GFXCubemapArray *createCubemapArray(); + + virtual F32 getFillConventionOffset() const { return 0.0f; } + + + ///@} + + /// @name Render Target functions + /// @{ + + /// + virtual GFXTextureTarget *allocRenderToTextureTarget(bool genMips = true); + virtual GFXWindowTarget *allocWindowTarget(PlatformWindow *window); + virtual void _updateRenderTargets(); + + ///@} + + /// @name Shader functions + /// @{ + virtual F32 getPixelShaderVersion() const { return mPixelShaderVersion; } + virtual void setPixelShaderVersion( F32 version ) { mPixelShaderVersion = version; } + + virtual void setShader(GFXShader *shader, bool force = false); + + /// @attention GL cannot check if the given format supports blending or filtering! + virtual GFXFormat selectSupportedFormat(GFXTextureProfile *profile, + const Vector &formats, bool texture, bool mustblend, bool mustfilter); + + /// Returns the number of texture samplers that can be used in a shader rendering pass + virtual U32 getNumSamplers() const; + + /// Returns the number of simultaneous render targets supported by the device. + virtual U32 getNumRenderTargets() const; + + virtual GFXShader* createShader(); + + virtual void clear( U32 flags, const LinearColorF& color, F32 z, U32 stencil ); + virtual void clearColorAttachment(const U32 attachment, const LinearColorF& color); + virtual bool beginSceneInternal(); + virtual void endSceneInternal(); + + virtual void drawPrimitive( GFXPrimitiveType primType, U32 vertexStart, U32 primitiveCount ); + + virtual void drawIndexedPrimitive( GFXPrimitiveType primType, + U32 startVertex, + U32 minIndex, + U32 numVerts, + U32 startIndex, + U32 primitiveCount ); + + virtual void setClipRect( const RectI &rect ); + virtual const RectI &getClipRect() const { return mClip; } + + virtual void preDestroy() { Parent::preDestroy(); } + + virtual U32 getMaxDynamicVerts() { return MAX_DYNAMIC_VERTS; } + virtual U32 getMaxDynamicIndices() { return MAX_DYNAMIC_INDICES; } + + GFXFence *createFence(); + + GFXOcclusionQuery* createOcclusionQuery(); + + GFXGLStateBlockRef getCurrentStateBlock() { return mCurrentGLStateBlock; } + + virtual void setupGenericShaders( GenericShaderType type = GSColor ); + + /// + bool supportsAnisotropic() const { return mSupportsAnisotropic; } + + GFXGLStateCache* getOpenglCache() { return mOpenglStateCache; } + + GFXTextureObject* getDefaultDepthTex() const; + + /// Returns the number of vertex streams supported by the device. + const U32 getNumVertexStreams() const { return mNumVertexStream; } + + bool glUseMap() const { return mUseGlMap; } + +protected: + /// Called by GFXDevice to create a device specific stateblock + virtual GFXStateBlockRef createStateBlockInternal(const GFXStateBlockDesc& desc); + /// Called by GFXDevice to actually set a stateblock. + virtual void setStateBlockInternal(GFXStateBlock* block, bool force); + + /// Called by base GFXDevice to actually set a const buffer + virtual void setShaderConstBufferInternal(GFXShaderConstBuffer* buffer); + + virtual void setTextureInternal(U32 textureUnit, const GFXTextureObject*texture); + virtual void setCubemapInternal(U32 textureUnit, const GFXGLCubemap* texture); + virtual void setCubemapArrayInternal(U32 textureUnit, const GFXGLCubemapArray* texture); + + virtual void setLightInternal(U32 lightStage, const GFXLightInfo light, bool lightEnable); + virtual void setLightMaterialInternal(const GFXLightMaterial mat); + virtual void setGlobalAmbientInternal(LinearColorF color); + + /// @name State Initalization. + /// @{ + + /// State initalization. This MUST BE CALLED in setVideoMode after the device + /// is created. + virtual void initStates() { } + + virtual void setMatrix( GFXMatrixType mtype, const MatrixF &mat ); + + virtual GFXVertexBuffer *allocVertexBuffer( U32 numVerts, + const GFXVertexFormat *vertexFormat, + U32 vertSize, + GFXBufferType bufferType, + void* data = NULL); + virtual GFXPrimitiveBuffer *allocPrimitiveBuffer( U32 numIndices, U32 numPrimitives, GFXBufferType bufferType, void* data = NULL ); + + // NOTE: The GL device doesn't need a vertex declaration at + // this time, but we need to return something to keep the system + // from retrying to allocate one on every call. + virtual GFXVertexDecl* allocVertexDecl( const GFXVertexFormat *vertexFormat ); + + virtual void setVertexDecl( const GFXVertexDecl *decl ); + + virtual void setVertexStream( U32 stream, GFXVertexBuffer *buffer ); + virtual void setVertexStreamFrequency( U32 stream, U32 frequency ); + +private: + typedef GFXDevice Parent; + + friend class GFXGLTextureObject; + friend class GFXGLCubemap; + friend class GFXGLCubemapArray; + friend class GFXGLWindowTarget; + friend class GFXGLPrimitiveBuffer; + friend class GFXGLVertexBuffer; + + static GFXAdapter::CreateDeviceInstanceDelegate mCreateDeviceInstance; + + U32 mAdapterIndex; + + StrongRefPtr mCurrentVB[VERTEX_STREAM_COUNT]; + U32 mCurrentVB_Divisor[VERTEX_STREAM_COUNT]; + bool mNeedUpdateVertexAttrib; + StrongRefPtr mCurrentPB; + U32 mDrawInstancesCount; + + GFXShader* mCurrentShader; + GFXShaderRef mGenericShader[GS_COUNT]; + GFXShaderConstBufferRef mGenericShaderBuffer[GS_COUNT]; + GFXShaderConstHandle *mModelViewProjSC[GS_COUNT]; + + /// Since GL does not have separate world and view matrices we need to track them + MatrixF m_mCurrentWorld; + MatrixF m_mCurrentView; + + void* mContext; + void* mPixelFormat; + + F32 mPixelShaderVersion; + + bool mSupportsAnisotropic; + + U32 mNumVertexStream; + + U32 mMaxShaderTextures; + U32 mMaxFFTextures; + + U32 mMaxTRColors; + + RectI mClip; + + GFXGLStateBlockRef mCurrentGLStateBlock; + + GLenum mActiveTextureType[TEXTURE_STAGE_COUNT]; + + Vector< StrongRefPtr > mVolatileVBs; ///< Pool of existing volatile VBs so we can reuse previously created ones + Vector< StrongRefPtr > mVolatilePBs; ///< Pool of existing volatile PBs so we can reuse previously created ones + + GLsizei primCountToIndexCount(GFXPrimitiveType primType, U32 primitiveCount); + void preDrawPrimitive(); + void postDrawPrimitive(U32 primitiveCount); + + GFXVertexBuffer* findVolatileVBO(U32 numVerts, const GFXVertexFormat *vertexFormat, U32 vertSize); ///< Returns an existing volatile VB which has >= numVerts and the same vert flags/size, or creates a new VB if necessary + GFXPrimitiveBuffer* findVolatilePBO(U32 numIndices, U32 numPrimitives); ///< Returns an existing volatile PB which has >= numIndices, or creates a new PB if necessary + + void vsyncCallback(); ///< Vsync callback + + void initGLState(); ///< Guaranteed to be called after all extensions have been loaded, use to init card profiler, shader version, max samplers, etc. + + GFXFence* _createPlatformSpecificFence(); ///< If our platform (e.g. OS X) supports a fence extenstion (e.g. GL_APPLE_fence) this will create one, otherwise returns NULL + + void setPB(GFXGLPrimitiveBuffer* pb); ///< Sets mCurrentPB + + GFXGLStateCache *mOpenglStateCache; + + GFXWindowTargetRef *mWindowRT; + + bool mUseGlMap; +}; + +#define GFXGL static_cast(GFXDevice::get()) +#endif diff --git a/gl/gfxGLShader.cpp b/gl/gfxGLShader.cpp new file mode 100644 index 000000000..a5079756f --- /dev/null +++ b/gl/gfxGLShader.cpp @@ -0,0 +1,1151 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gl/gfxGLShader.h" +#include "gfx/gl/gfxGLVertexAttribLocation.h" +#include "gfx/gl/gfxGLDevice.h" + +#include "core/frameAllocator.h" +#include "core/stream/fileStream.h" +#include "core/strings/stringFunctions.h" +#include "math/mPoint2.h" +#include "gfx/gfxStructs.h" +#include "console/console.h" + +#define CHECK_AARG(pos, name) static StringTableEntry attr_##name = StringTable->insert(#name); if (argName == attr_##name) { glBindAttribLocation(mProgram, pos, attr_##name); continue; } + +class GFXGLShaderConstHandle : public GFXShaderConstHandle +{ + friend class GFXGLShader; + +public: + + GFXGLShaderConstHandle( GFXGLShader *shader ); + GFXGLShaderConstHandle( GFXGLShader *shader, const GFXShaderConstDesc &desc, GLuint loc, S32 samplerNum ); + virtual ~GFXGLShaderConstHandle(); + + void reinit( const GFXShaderConstDesc &desc, GLuint loc, S32 samplerNum ); + + const String& getName() const { return mDesc.name; } + GFXShaderConstType getType() const { return mDesc.constType; } + U32 getArraySize() const { return mDesc.arraySize; } + + U32 getSize() const; + void setValid( bool valid ) { mValid = valid; } + /// @warning This will always return the value assigned when the shader was + /// initialized. If the value is later changed this method won't reflect that. + S32 getSamplerRegister() const { return mSamplerNum; } + + GFXShaderConstDesc mDesc; + GFXGLShader* mShader; + GLuint mLocation; + U32 mOffset; + U32 mSize; + S32 mSamplerNum; + bool mInstancingConstant; +}; + +GFXGLShaderConstHandle::GFXGLShaderConstHandle( GFXGLShader *shader ) + : mShader( shader ), mLocation(0), mOffset(0), mSize(0), mSamplerNum(-1), mInstancingConstant(false) +{ + mValid = false; +} + +static U32 shaderConstTypeSize(GFXShaderConstType type) +{ + switch(type) + { + case GFXSCT_Float: + case GFXSCT_Int: + case GFXSCT_Sampler: + case GFXSCT_SamplerCube: + case GFXSCT_SamplerCubeArray: + return 4; + case GFXSCT_Float2: + case GFXSCT_Int2: + return 8; + case GFXSCT_Float3: + case GFXSCT_Int3: + return 12; + case GFXSCT_Float4: + case GFXSCT_Int4: + return 16; + case GFXSCT_Float2x2: + return 16; + case GFXSCT_Float3x3: + return 36; + case GFXSCT_Float4x3: + return 48; + case GFXSCT_Float4x4: + return 64; + default: + AssertFatal(false,"shaderConstTypeSize - Unrecognized constant type"); + return 0; + } +} + +GFXGLShaderConstHandle::GFXGLShaderConstHandle( GFXGLShader *shader, const GFXShaderConstDesc &desc, GLuint loc, S32 samplerNum ) + : mShader(shader), mInstancingConstant(false) +{ + reinit(desc, loc, samplerNum); +} + +void GFXGLShaderConstHandle::reinit( const GFXShaderConstDesc& desc, GLuint loc, S32 samplerNum ) +{ + mDesc = desc; + mLocation = loc; + mSamplerNum = samplerNum; + mOffset = 0; + mInstancingConstant = false; + + U32 elemSize = shaderConstTypeSize(mDesc.constType); + AssertFatal(elemSize, "GFXGLShaderConst::GFXGLShaderConst - elemSize is 0"); + mSize = mDesc.arraySize * elemSize; + mValid = true; +} + + +U32 GFXGLShaderConstHandle::getSize() const +{ + return mSize; +} + +GFXGLShaderConstHandle::~GFXGLShaderConstHandle() +{ +} + +GFXGLShaderConstBuffer::GFXGLShaderConstBuffer(GFXGLShader* shader, U32 bufSize, U8* existingConstants) +{ + mShader = shader; + mBuffer = new U8[bufSize]; + mWasLost = true; + + // Copy the existing constant buffer to preserve sampler numbers + /// @warning This preserves a lot more than sampler numbers, obviously. If there + /// is any code that assumes a new constant buffer will have everything set to + /// 0, it will break. + dMemcpy(mBuffer, existingConstants, bufSize); +} + +GFXGLShaderConstBuffer::~GFXGLShaderConstBuffer() +{ + delete[] mBuffer; + + if ( mShader ) + mShader->_unlinkBuffer( this ); +} + +template +void GFXGLShaderConstBuffer::internalSet(GFXShaderConstHandle* handle, const ConstType& param) +{ + AssertFatal(handle, "GFXGLShaderConstBuffer::internalSet - Handle is NULL!" ); + AssertFatal(handle->isValid(), "GFXGLShaderConstBuffer::internalSet - Handle is not valid!" ); + AssertFatal(dynamic_cast(handle), "GFXGLShaderConstBuffer::set - Incorrect const buffer type"); + + GFXGLShaderConstHandle* _glHandle = static_cast(handle); + AssertFatal(mShader == _glHandle->mShader, "GFXGLShaderConstBuffer::set - Should only set handles which are owned by our shader"); + U8 *buf = mBuffer + _glHandle->mOffset; + + if(_glHandle->mInstancingConstant) + buf = mInstPtr + _glHandle->mOffset; + + dMemcpy(buf, ¶m, sizeof(ConstType)); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const F32 fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point2F& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point3F& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point4F& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const PlaneF& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const LinearColorF& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const S32 fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point2I& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point3I& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point4I& fv) +{ + internalSet(handle, fv); +} + +template +void GFXGLShaderConstBuffer::internalSet(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + AssertFatal(handle, "GFXGLShaderConstBuffer::internalSet - Handle is NULL!" ); + AssertFatal(handle->isValid(), "GFXGLShaderConstBuffer::internalSet - Handle is not valid!" ); + AssertFatal(dynamic_cast(handle), "GFXGLShaderConstBuffer::set - Incorrect const buffer type"); + + GFXGLShaderConstHandle* _glHandle = static_cast(handle); + AssertFatal(mShader == _glHandle->mShader, "GFXGLShaderConstBuffer::set - Should only set handles which are owned by our shader"); + AssertFatal(!_glHandle->mInstancingConstant, "GFXGLShaderConstBuffer::set - Instancing not supported for array"); + const U8* fvBuffer = static_cast(fv.getBuffer()); + for(U32 i = 0; i < fv.size(); ++i) + { + dMemcpy(mBuffer + _glHandle->mOffset + i * sizeof(ConstType), fvBuffer, sizeof(ConstType)); + fvBuffer += fv.getElementSize(); + } +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const MatrixF& mat, const GFXShaderConstType matType) +{ + AssertFatal(handle, "GFXGLShaderConstBuffer::set - Handle is NULL!" ); + AssertFatal(handle->isValid(), "GFXGLShaderConstBuffer::set - Handle is not valid!" ); + AssertFatal(dynamic_cast(handle), "GFXGLShaderConstBuffer::set - Incorrect const buffer type"); + + GFXGLShaderConstHandle* _glHandle = static_cast(handle); + AssertFatal(mShader == _glHandle->mShader, "GFXGLShaderConstBuffer::set - Should only set handles which are owned by our shader"); + AssertFatal(!_glHandle->mInstancingConstant || matType == GFXSCT_Float4x4, "GFXGLShaderConstBuffer::set - Only support GFXSCT_Float4x4 for instancing"); + + switch(matType) + { + case GFXSCT_Float2x2: + reinterpret_cast(mBuffer + _glHandle->mOffset)[0] = mat[0]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[1] = mat[1]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[2] = mat[4]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[3] = mat[5]; + break; + case GFXSCT_Float3x3: + reinterpret_cast(mBuffer + _glHandle->mOffset)[0] = mat[0]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[1] = mat[1]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[2] = mat[2]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[3] = mat[4]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[4] = mat[5]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[5] = mat[6]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[6] = mat[8]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[7] = mat[9]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[8] = mat[10]; + break; + case GFXSCT_Float4x3: + dMemcpy(mBuffer + _glHandle->mOffset, (const F32*)mat, (sizeof(F32) * 12));// matrix with end row chopped off + break; + case GFXSCT_Float4x4: + { + if(_glHandle->mInstancingConstant) + { + MatrixF transposed; + mat.transposeTo(transposed); + dMemcpy( mInstPtr + _glHandle->mOffset, (const F32*)transposed, sizeof(MatrixF) ); + return; + } + + dMemcpy(mBuffer + _glHandle->mOffset, (const F32*)mat, sizeof(MatrixF)); + break; + } + default: + AssertFatal(false, "GFXGLShaderConstBuffer::set - Invalid matrix type"); + break; + } +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const MatrixF* mat, const U32 arraySize, const GFXShaderConstType matrixType) +{ + AssertFatal(handle, "GFXGLShaderConstBuffer::set - Handle is NULL!" ); + AssertFatal(handle->isValid(), "GFXGLShaderConstBuffer::set - Handle is not valid!" ); + + GFXGLShaderConstHandle* _glHandle = static_cast(handle); + AssertFatal(mShader == _glHandle->mShader, "GFXGLShaderConstBuffer::set - Should only set handles which are owned by our shader"); + AssertFatal(!_glHandle->mInstancingConstant, "GFXGLShaderConstBuffer::set - Instancing not supported for matrix arrays"); + + switch (matrixType) { + case GFXSCT_Float4x3: + // Copy each item with the last row chopped off + for (int i = 0; imOffset + (i*(sizeof(F32) * 12)), (F32*)(mat + i), sizeof(F32) * 12); + } + break; + case GFXSCT_Float4x4: + dMemcpy(mBuffer + _glHandle->mOffset, (F32*)mat, _glHandle->getSize()); + break; + default: + AssertFatal(false, "GFXGLShaderConstBuffer::set - setting array of non 4x4 matrices!"); + break; + } +} + +void GFXGLShaderConstBuffer::activate() +{ + PROFILE_SCOPE(GFXGLShaderConstBuffer_activate); + mShader->setConstantsFromBuffer(this); + mWasLost = false; +} + +const String GFXGLShaderConstBuffer::describeSelf() const +{ + return String(); +} + +void GFXGLShaderConstBuffer::onShaderReload( GFXGLShader *shader ) +{ + AssertFatal( shader == mShader, "GFXGLShaderConstBuffer::onShaderReload, mismatched shaders!" ); + + delete[] mBuffer; + mBuffer = new U8[mShader->mConstBufferSize]; + dMemset(mBuffer, 0, mShader->mConstBufferSize); + mWasLost = true; +} + +GFXGLShader::GFXGLShader() : + mVertexShader(0), + mPixelShader(0), + mProgram(0), + mConstBufferSize(0), + mConstBuffer(NULL) +{ +} + +GFXGLShader::~GFXGLShader() +{ + clearShaders(); + for(HandleMap::Iterator i = mHandles.begin(); i != mHandles.end(); i++) + delete i->value; + + delete[] mConstBuffer; +} + +void GFXGLShader::clearShaders() +{ + glDeleteProgram(mProgram); + glDeleteShader(mVertexShader); + glDeleteShader(mPixelShader); + + mProgram = 0; + mVertexShader = 0; + mPixelShader = 0; +} + +bool GFXGLShader::_init() +{ + PROFILE_SCOPE(GFXGLShader_Init); + // Don't initialize empty shaders. + if ( mVertexFile.isEmpty() && mPixelFile.isEmpty() ) + return false; + + clearShaders(); + + mProgram = glCreateProgram(); + + // Set the macros and add the global ones. + Vector macros; + macros.merge( mMacros ); + macros.merge( smGlobalMacros ); + + macros.increment(); + macros.last().name = "TORQUE_SM"; + macros.last().value = 40; + macros.increment(); + macros.last().name = "TORQUE_VERTEX_SHADER"; + macros.last().value = ""; + + // Default to true so we're "successful" if a vertex/pixel shader wasn't specified. + bool compiledVertexShader = true; + bool compiledPixelShader = true; + + // Compile the vertex and pixel shaders if specified. + if(!mVertexFile.isEmpty()) + compiledVertexShader = initShader(mVertexFile, true, macros); + + macros.last().name = "TORQUE_PIXEL_SHADER"; + if(!mPixelFile.isEmpty()) + compiledPixelShader = initShader(mPixelFile, false, macros); + + // If either shader was present and failed to compile, bail. + if(!compiledVertexShader || !compiledPixelShader) + return false; + + // Link it! + glLinkProgram( mProgram ); + + GLint activeAttribs = 0; + glGetProgramiv(mProgram, GL_ACTIVE_ATTRIBUTES, &activeAttribs ); + + GLint maxLength; + glGetProgramiv(mProgram, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxLength); + + FrameTemp tempData(maxLength+1); + *tempData.address() = '\0'; + // Check atributes + for (U32 i=0; iinsert(tempData.address()); + + CHECK_AARG(Torque::GL_VertexAttrib_Position, vPosition); + CHECK_AARG(Torque::GL_VertexAttrib_Normal, vNormal); + CHECK_AARG(Torque::GL_VertexAttrib_Color, vColor); + CHECK_AARG(Torque::GL_VertexAttrib_Tangent, vTangent); + CHECK_AARG(Torque::GL_VertexAttrib_TangentW, vTangentW); + CHECK_AARG(Torque::GL_VertexAttrib_Binormal, vBinormal); + CHECK_AARG(Torque::GL_VertexAttrib_TexCoord0, vTexCoord0); + CHECK_AARG(Torque::GL_VertexAttrib_TexCoord1, vTexCoord1); + CHECK_AARG(Torque::GL_VertexAttrib_TexCoord2, vTexCoord2); + CHECK_AARG(Torque::GL_VertexAttrib_TexCoord3, vTexCoord3); + CHECK_AARG(Torque::GL_VertexAttrib_TexCoord4, vTexCoord4); + CHECK_AARG(Torque::GL_VertexAttrib_TexCoord5, vTexCoord5); + CHECK_AARG(Torque::GL_VertexAttrib_TexCoord6, vTexCoord6); + CHECK_AARG(Torque::GL_VertexAttrib_TexCoord7, vTexCoord7); + CHECK_AARG(Torque::GL_VertexAttrib_TexCoord8, vTexCoord8); + CHECK_AARG(Torque::GL_VertexAttrib_TexCoord9, vTexCoord9); + } + + //always have OUT_col + glBindFragDataLocation(mProgram, 0, "OUT_col"); + // Check OUT_colN + for(U32 i=1;i<4;i++) + { + char buffer[10]; + dSprintf(buffer, sizeof(buffer), "OUT_col%u",i); + GLint location = glGetFragDataLocation(mProgram, buffer); + if(location>0) + glBindFragDataLocation(mProgram, i, buffer); + + } + + // Link it again! + glLinkProgram( mProgram ); + + GLint linkStatus; + glGetProgramiv( mProgram, GL_LINK_STATUS, &linkStatus ); + + // Dump the info log to the console + U32 logLength = 0; + glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, (GLint*)&logLength); + if ( logLength ) + { + FrameAllocatorMarker fam; + char* log = (char*)fam.alloc( logLength ); + glGetProgramInfoLog( mProgram, logLength, NULL, log ); + + if ( linkStatus == GL_FALSE ) + { + if ( smLogErrors ) + { + Con::errorf( "GFXGLShader::init - Error linking shader!" ); + Con::errorf( "Program %s / %s: %s", + mVertexFile.getFullPath().c_str(), mPixelFile.getFullPath().c_str(), log); + } + } + else if ( smLogWarnings ) + { + Con::warnf( "Program %s / %s: %s", + mVertexFile.getFullPath().c_str(), mPixelFile.getFullPath().c_str(), log); + } + } + + + // If we failed to link, bail. + if ( linkStatus == GL_FALSE ) + return false; + + initConstantDescs(); + initHandles(); + + // Notify Buffers we might have changed in size. + // If this was our first init then we won't have any activeBuffers + // to worry about unnecessarily calling. + Vector::iterator biter = mActiveBuffers.begin(); + for ( ; biter != mActiveBuffers.end(); biter++ ) + ((GFXGLShaderConstBuffer*)(*biter))->onShaderReload( this ); + + return true; +} + + +void GFXGLShader::initConstantDescs() +{ + mConstants.clear(); + GLint numUniforms; + glGetProgramiv(mProgram, GL_ACTIVE_UNIFORMS, &numUniforms); + GLint maxNameLength; + glGetProgramiv(mProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxNameLength); + + if(!maxNameLength) + return; + + FrameTemp uniformName(maxNameLength); + + for(U32 i = 0; i < numUniforms; i++) + { + GLint size; + GLenum type; + glGetActiveUniform(mProgram, i, maxNameLength, NULL, &size, &type, uniformName); + GFXShaderConstDesc desc; + + desc.name = String((char*)uniformName); + + // Remove array brackets from the name + desc.name = desc.name.substr(0, desc.name.find('[')); + + // Insert $ to match D3D behavior of having a $ prepended to parameters to main. + desc.name.insert(0, '$'); + desc.arraySize = size; + + switch(type) + { + case GL_FLOAT: + desc.constType = GFXSCT_Float; + break; + case GL_FLOAT_VEC2: + desc.constType = GFXSCT_Float2; + break; + case GL_FLOAT_VEC3: + desc.constType = GFXSCT_Float3; + break; + case GL_FLOAT_VEC4: + desc.constType = GFXSCT_Float4; + break; + case GL_INT: + desc.constType = GFXSCT_Int; + break; + case GL_INT_VEC2: + desc.constType = GFXSCT_Int2; + break; + case GL_INT_VEC3: + desc.constType = GFXSCT_Int3; + break; + case GL_INT_VEC4: + desc.constType = GFXSCT_Int4; + break; + case GL_FLOAT_MAT2: + desc.constType = GFXSCT_Float2x2; + break; + case GL_FLOAT_MAT3: + desc.constType = GFXSCT_Float3x3; + break; + case GL_FLOAT_MAT4: + desc.constType = GFXSCT_Float4x4; + break; + case GL_FLOAT_MAT4x3: // jamesu - columns, rows + desc.constType = GFXSCT_Float4x3; + break; + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + desc.constType = GFXSCT_Sampler; + break; + case GL_SAMPLER_CUBE: + desc.constType = GFXSCT_SamplerCube; + break; + case GL_SAMPLER_CUBE_MAP_ARRAY: + desc.constType = GFXSCT_SamplerCubeArray; + break; + default: + AssertFatal(false, "GFXGLShader::initConstantDescs - unrecognized uniform type"); + // If we don't recognize the constant don't add its description. + continue; + } + + mConstants.push_back(desc); + } +} + +void GFXGLShader::initHandles() +{ + // Mark all existing handles as invalid. + // Those that are found when parsing the descriptions will then be marked valid again. + for ( HandleMap::Iterator iter = mHandles.begin(); iter != mHandles.end(); ++iter ) + (iter->value)->setValid( false ); + mValidHandles.clear(); + + // Loop through all ConstantDescriptions, + // if they aren't in the HandleMap add them, if they are reinitialize them. + for ( U32 i = 0; i < mConstants.size(); i++ ) + { + GFXShaderConstDesc &desc = mConstants[i]; + + // Index element 1 of the name to skip the '$' we inserted earier. + GLint loc = glGetUniformLocation(mProgram, &desc.name.c_str()[1]); + + AssertFatal(loc != -1, ""); + + HandleMap::Iterator handle = mHandles.find(desc.name); + S32 sampler = -1; + if(desc.constType == GFXSCT_Sampler || desc.constType == GFXSCT_SamplerCube || desc.constType == GFXSCT_SamplerCubeArray) + { + S32 idx = mSamplerNamesOrdered.find_next(desc.name); + AssertFatal(idx != -1, ""); + sampler = idx; //assignedSamplerNum++; + } + if ( handle != mHandles.end() ) + { + handle->value->reinit( desc, loc, sampler ); + } + else + { + mHandles[desc.name] = new GFXGLShaderConstHandle( this, desc, loc, sampler ); + } + } + + // Loop through handles once more to set their offset and calculate our + // constBuffer size. + + if ( mConstBuffer ) + delete[] mConstBuffer; + mConstBufferSize = 0; + + for ( HandleMap::Iterator iter = mHandles.begin(); iter != mHandles.end(); ++iter ) + { + GFXGLShaderConstHandle* handle = iter->value; + if ( handle->isValid() ) + { + mValidHandles.push_back(handle); + handle->mOffset = mConstBufferSize; + mConstBufferSize += handle->getSize(); + } + } + + mConstBuffer = new U8[mConstBufferSize]; + dMemset(mConstBuffer, 0, mConstBufferSize); + + // Set our program so uniforms are assigned properly. + glUseProgram(mProgram); + // Iterate through uniforms to set sampler numbers. + for (HandleMap::Iterator iter = mHandles.begin(); iter != mHandles.end(); ++iter) + { + GFXGLShaderConstHandle* handle = iter->value; + if(handle->isValid() && (handle->getType() == GFXSCT_Sampler || handle->getType() == GFXSCT_SamplerCube || handle->getType() == GFXSCT_SamplerCubeArray)) + { + // Set sampler number on our program. + glUniform1i(handle->mLocation, handle->mSamplerNum); + // Set sampler in constant buffer so it does not get unset later. + dMemcpy(mConstBuffer + handle->mOffset, &handle->mSamplerNum, handle->getSize()); + } + } + glUseProgram(0); + + //instancing + if (!mInstancingFormat) + return; + + U32 offset = 0; + + for ( U32 i=0; i < mInstancingFormat->getElementCount(); i++ ) + { + const GFXVertexElement &element = mInstancingFormat->getElement( i ); + + String constName = String::ToString( "$%s", element.getSemantic().c_str() ); + + HandleMap::Iterator handle = mHandles.find(constName); + if ( handle != mHandles.end() ) + { + AssertFatal(0, ""); + } + else + { + GFXShaderConstDesc desc; + desc.name = constName; + desc.arraySize = 1; + switch(element.getType()) + { + case GFXDeclType_Float4: + desc.constType = GFXSCT_Float4; + break; + + default: + desc.constType = GFXSCT_Float; + break; + } + + GFXGLShaderConstHandle *h = new GFXGLShaderConstHandle( this, desc, -1, -1 ); + h->mInstancingConstant = true; + h->mOffset = offset; + mHandles[constName] = h; + + offset += element.getSizeInBytes(); + ++i; + + // If this is a matrix we will have 2 or 3 more of these + // semantics with the same name after it. + for ( ; i < mInstancingFormat->getElementCount(); i++ ) + { + const GFXVertexElement &nextElement = mInstancingFormat->getElement( i ); + if ( nextElement.getSemantic() != element.getSemantic() ) + { + i--; + break; + } + ++desc.arraySize; + if(desc.arraySize == 4 && desc.constType == GFXSCT_Float4) + { + desc.arraySize = 1; + desc.constType = GFXSCT_Float4x4; + } + offset += nextElement.getSizeInBytes(); + } + } + + } +} + +GFXShaderConstHandle* GFXGLShader::getShaderConstHandle(const String& name) +{ + HandleMap::Iterator i = mHandles.find(name); + if(i != mHandles.end()) + return i->value; + else + { + GFXGLShaderConstHandle* handle = new GFXGLShaderConstHandle( this ); + mHandles[ name ] = handle; + + return handle; + } +} + +GFXShaderConstHandle* GFXGLShader::findShaderConstHandle(const String& name) +{ + HandleMap::Iterator i = mHandles.find(name); + if(i != mHandles.end()) + return i->value; + else + { + return NULL; + } +} + +void GFXGLShader::setConstantsFromBuffer(GFXGLShaderConstBuffer* buffer) +{ + for(Vector::iterator i = mValidHandles.begin(); i != mValidHandles.end(); ++i) + { + GFXGLShaderConstHandle* handle = *i; + AssertFatal(handle, "GFXGLShader::setConstantsFromBuffer - Null handle"); + + if(handle->mInstancingConstant) + continue; + + // Don't set if the value has not be changed. + if(dMemcmp(mConstBuffer + handle->mOffset, buffer->mBuffer + handle->mOffset, handle->getSize()) == 0) + continue; + + // Copy new value into our const buffer and set in GL. + dMemcpy(mConstBuffer + handle->mOffset, buffer->mBuffer + handle->mOffset, handle->getSize()); + switch(handle->mDesc.constType) + { + case GFXSCT_Float: + glUniform1fv(handle->mLocation, handle->mDesc.arraySize, (GLfloat*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Float2: + glUniform2fv(handle->mLocation, handle->mDesc.arraySize, (GLfloat*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Float3: + glUniform3fv(handle->mLocation, handle->mDesc.arraySize, (GLfloat*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Float4: + glUniform4fv(handle->mLocation, handle->mDesc.arraySize, (GLfloat*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Int: + case GFXSCT_Sampler: + case GFXSCT_SamplerCube: + case GFXSCT_SamplerCubeArray: + glUniform1iv(handle->mLocation, handle->mDesc.arraySize, (GLint*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Int2: + glUniform2iv(handle->mLocation, handle->mDesc.arraySize, (GLint*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Int3: + glUniform3iv(handle->mLocation, handle->mDesc.arraySize, (GLint*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Int4: + glUniform4iv(handle->mLocation, handle->mDesc.arraySize, (GLint*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Float2x2: + glUniformMatrix2fv(handle->mLocation, handle->mDesc.arraySize, true, (GLfloat*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Float3x3: + glUniformMatrix3fv(handle->mLocation, handle->mDesc.arraySize, true, (GLfloat*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Float4x3: + // NOTE: To save a transpose here we could store the matrix transposed (i.e. column major) in the constant buffer. + // See _mesa_uniform_matrix in the mesa source for the correct transpose algorithm for a 4x3 matrix. + glUniformMatrix4x3fv(handle->mLocation, handle->mDesc.arraySize, true, (GLfloat*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Float4x4: + glUniformMatrix4fv(handle->mLocation, handle->mDesc.arraySize, true, (GLfloat*)(mConstBuffer + handle->mOffset)); + break; + default: + AssertFatal(0,""); + break; + } + } +} + +GFXShaderConstBufferRef GFXGLShader::allocConstBuffer() +{ + GFXGLShaderConstBuffer* buffer = new GFXGLShaderConstBuffer(this, mConstBufferSize, mConstBuffer); + buffer->registerResourceWithDevice(getOwningDevice()); + mActiveBuffers.push_back( buffer ); + return buffer; +} + +void GFXGLShader::useProgram() +{ + glUseProgram(mProgram); +} + +void GFXGLShader::zombify() +{ + clearShaders(); + dMemset(mConstBuffer, 0, mConstBufferSize); +} + +char* GFXGLShader::_handleIncludes( const Torque::Path& path, FileStream *s ) +{ + // TODO: The #line pragma on GLSL takes something called a + // "source-string-number" which it then never explains. + // + // Until i resolve this mystery i disabled this. + // + //String linePragma = String::ToString( "#line 1 \r\n"); + //U32 linePragmaLen = linePragma.length(); + + U32 shaderLen = s->getStreamSize(); + char* buffer = (char*)dMalloc(shaderLen + 1); + //dStrncpy( buffer, linePragma.c_str(), linePragmaLen ); + s->read(shaderLen, buffer); + buffer[shaderLen] = 0; + + char* p = dStrstr(buffer, "#include"); + while(p) + { + char* q = p; + p += 8; + if(dIsspace(*p)) + { + U32 n = 0; + while(dIsspace(*p)) ++p; + AssertFatal(*p == '"', "Bad #include directive"); + ++p; + static char includeFile[256]; + while(*p != '"') + { + AssertFatal(*p != 0, "Bad #include directive"); + includeFile[n++] = *p++; + AssertFatal(n < sizeof(includeFile), "#include directive too long"); + } + ++p; + includeFile[n] = 0; + + // First try it as a local file. + Torque::Path includePath = Torque::Path::Join(path.getPath(), '/', includeFile); + includePath = Torque::Path::CompressPath(includePath); + + FileStream includeStream; + + if ( !includeStream.open( includePath, Torque::FS::File::Read ) ) + { + // Try again assuming the path is absolute + // and/or relative. + includePath = String( includeFile ); + includePath = Torque::Path::CompressPath(includePath); + if ( !includeStream.open( includePath, Torque::FS::File::Read ) ) + { + AssertISV(false, avar("failed to open include '%s'.", includePath.getFullPath().c_str())); + + if ( smLogErrors ) + Con::errorf( "GFXGLShader::_handleIncludes - Failed to open include '%s'.", + includePath.getFullPath().c_str() ); + + // Fail... don't return the buffer. + dFree(buffer); + return NULL; + } + } + + char* includedText = _handleIncludes(includePath, &includeStream); + + // If a sub-include fails... cleanup and return. + if ( !includedText ) + { + dFree(buffer); + return NULL; + } + + // TODO: Disabled till this is fixed correctly. + // + // Count the number of lines in the file + // before the include. + /* + U32 includeLine = 0; + { + char* nl = dStrstr( buffer, "\n" ); + while ( nl ) + { + includeLine++; + nl = dStrstr( nl, "\n" ); + if(nl) ++nl; + } + } + */ + + String manip(buffer); + manip.erase(q-buffer, p-q); + String sItx(includedText); + + // TODO: Disabled till this is fixed correctly. + // + // Add a new line pragma to restore the proper + // file and line number after the include. + //sItx += String::ToString( "\r\n#line %d \r\n", includeLine ); + + dFree(includedText); + manip.insert(q-buffer, sItx); + char* manipBuf = dStrdup(manip.c_str()); + p = manipBuf + (q - buffer); + dFree(buffer); + buffer = manipBuf; + } + p = dStrstr(p, "#include"); + } + + return buffer; +} + +bool GFXGLShader::_loadShaderFromStream( GLuint shader, + const Torque::Path &path, + FileStream *s, + const Vector ¯os ) +{ + Vector buffers; + Vector lengths; + + // The GLSL version declaration must go first! + const char *versionDecl = "#version 150\r\n"; + buffers.push_back( dStrdup( versionDecl ) ); + lengths.push_back( dStrlen( versionDecl ) ); + + if(GFXGL->mCapabilities.shaderModel5) + { + const char *extension = "#extension GL_ARB_gpu_shader5 : enable\r\n"; + buffers.push_back( dStrdup( extension ) ); + lengths.push_back( dStrlen( extension ) ); + } + + const char *newLine = "\r\n"; + buffers.push_back( dStrdup( newLine ) ); + lengths.push_back( dStrlen( newLine ) ); + + // Now add all the macros. + for( U32 i = 0; i < macros.size(); i++ ) + { + if(macros[i].name.isEmpty()) // TODO OPENGL + continue; + + String define = String::ToString( "#define %s %s\n", macros[i].name.c_str(), macros[i].value.c_str() ); + buffers.push_back( dStrdup( define.c_str() ) ); + lengths.push_back( define.length() ); + } + + // Now finally add the shader source. + U32 shaderLen = s->getStreamSize(); + char *buffer = _handleIncludes(path, s); + if ( !buffer ) + return false; + + buffers.push_back(buffer); + lengths.push_back(shaderLen); + + glShaderSource(shader, buffers.size(), (const GLchar**)const_cast(buffers.address()), NULL); + +#if defined(TORQUE_DEBUG) && defined(TORQUE_DEBUG_GFX) + FileStream stream; + if ( !stream.open( path.getFullPath()+"_DEBUG", Torque::FS::File::Write ) ) + { + AssertISV(false, avar("GFXGLShader::initShader - failed to write debug shader '%s'.", path.getFullPath().c_str())); + } + + for(int i = 0; i < buffers.size(); ++i) + stream.writeText(buffers[i]); +#endif + + // Cleanup the shader source buffer. + for ( U32 i=0; i < buffers.size(); i++ ) + dFree( buffers[i] ); + + glCompileShader(shader); + + return true; +} + +bool GFXGLShader::initShader( const Torque::Path &file, + bool isVertex, + const Vector ¯os ) +{ + PROFILE_SCOPE(GFXGLShader_CompileShader); + GLuint activeShader = glCreateShader(isVertex ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER); + if(isVertex) + mVertexShader = activeShader; + else + mPixelShader = activeShader; + glAttachShader(mProgram, activeShader); + + + // Ok it's not in the shader gen manager, so ask Torque for it + FileStream stream; + if ( !stream.open( file, Torque::FS::File::Read ) ) + { + AssertISV(false, avar("GFXGLShader::initShader - failed to open shader '%s'.", file.getFullPath().c_str())); + + if ( smLogErrors ) + Con::errorf( "GFXGLShader::initShader - Failed to open shader file '%s'.", + file.getFullPath().c_str() ); + + return false; + } + + if ( !_loadShaderFromStream( activeShader, file, &stream, macros ) ) + return false; + + GLint compile; + glGetShaderiv(activeShader, GL_COMPILE_STATUS, &compile); + + // Dump the info log to the console + U32 logLength = 0; + glGetShaderiv(activeShader, GL_INFO_LOG_LENGTH, (GLint*)&logLength); + + GLint compileStatus = GL_TRUE; + if ( logLength ) + { + FrameAllocatorMarker fam; + char* log = (char*)fam.alloc(logLength); + glGetShaderInfoLog(activeShader, logLength, NULL, log); + + // Always print errors + glGetShaderiv( activeShader, GL_COMPILE_STATUS, &compileStatus ); + + if ( compileStatus == GL_FALSE ) + { + if ( smLogErrors ) + { + Con::errorf( "GFXGLShader::initShader - Error compiling shader!" ); + Con::errorf( "Program %s: %s", file.getFullPath().c_str(), log ); + } + } + else if ( smLogWarnings ) + Con::warnf( "Program %s: %s", file.getFullPath().c_str(), log ); + } + + return compileStatus != GL_FALSE; +} + +/// Returns our list of shader constants, the material can get this and just set the constants it knows about +const Vector& GFXGLShader::getShaderConstDesc() const +{ + PROFILE_SCOPE(GFXGLShader_GetShaderConstants); + return mConstants; +} + +/// Returns the alignment value for constType +U32 GFXGLShader::getAlignmentValue(const GFXShaderConstType constType) const +{ + // Alignment is the same thing as size for us. + return shaderConstTypeSize(constType); +} + +const String GFXGLShader::describeSelf() const +{ + String ret; + ret = String::ToString(" Program: %i", mProgram); + ret += String::ToString(" Vertex Path: %s", mVertexFile.getFullPath().c_str()); + ret += String::ToString(" Pixel Path: %s", mPixelFile.getFullPath().c_str()); + + return ret; +} diff --git a/gl/gfxGLStateCache.h b/gl/gfxGLStateCache.h new file mode 100644 index 000000000..24b3179e4 --- /dev/null +++ b/gl/gfxGLStateCache.h @@ -0,0 +1,141 @@ +#ifndef GFX_GL_STATE_CACHE +#define GFX_GL_STATE_CACHE + + +/// GFXGLStateCache store OpenGL state to avoid performance penalities of glGet* calls +/// GL_TEXTURE_1D/2D/3D, GL_FRAMEBUFFER, GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER +class GFXGLStateCache +{ +public: + GFXGLStateCache() + { + mActiveTexture = 0; + mBindedVBO = 0; + mBindedIBO = 0; + mBindedFBO_W = 0; + mBindedFBO_R = 0; + mVertexAttribActive = 0; + } + + class TextureUnit + { + public: + TextureUnit() : mTexture1D(0), mTexture2D(0), mTexture3D(0), mTextureCube(0), mTextureCubeArray(0) + { + + } + GLuint mTexture1D, mTexture2D, mTexture3D, mTextureCube, mTextureCubeArray; + }; + + /// after glBindTexture + void setCacheBindedTex(U32 texUnit, GLenum biding, GLuint handle) + { + mActiveTexture = texUnit; + switch (biding) + { + case GL_TEXTURE_2D: + mTextureUnits[mActiveTexture].mTexture2D = handle; + break; + case GL_TEXTURE_3D: + mTextureUnits[mActiveTexture].mTexture3D = handle; + break; + case GL_TEXTURE_1D: + mTextureUnits[mActiveTexture].mTexture1D = handle; + break; + case GL_TEXTURE_CUBE_MAP: + mTextureUnits[mActiveTexture].mTextureCube = handle; + break; + case GL_TEXTURE_CUBE_MAP_ARRAY: + mTextureUnits[mActiveTexture].mTextureCubeArray = handle; + break; + default: + AssertFatal(0, avar("GFXGLStateCache::setCacheBindedTex - binding (%x) not supported.", biding) ); + return; + } + } + + /// after opengl object binded + void setCacheBinded(GLenum biding, GLuint handle) + { + switch (biding) + { + case GL_TEXTURE_2D: + mTextureUnits[mActiveTexture].mTexture2D = handle; + break; + case GL_TEXTURE_3D: + mTextureUnits[mActiveTexture].mTexture3D = handle; + break; + case GL_TEXTURE_1D: + mTextureUnits[mActiveTexture].mTexture1D = handle; + break; + case GL_TEXTURE_CUBE_MAP: + mTextureUnits[mActiveTexture].mTextureCube = handle; + break; + case GL_TEXTURE_CUBE_MAP_ARRAY: + mTextureUnits[mActiveTexture].mTextureCubeArray = handle; + break; + case GL_FRAMEBUFFER: + mBindedFBO_W = mBindedFBO_R = handle; + break; + case GL_DRAW_FRAMEBUFFER: + mBindedFBO_W = handle; + break; + case GL_READ_FRAMEBUFFER: + mBindedFBO_R = handle; + break; + case GL_ARRAY_BUFFER: + mBindedVBO = handle; + break; + case GL_ELEMENT_ARRAY_BUFFER: + mBindedIBO = handle; + break; + default: + AssertFatal(0, avar("GFXGLStateCache::setCacheBinded - binding (%x) not supported.", biding) ); + break; + } + } + + GLuint getCacheBinded(GLenum biding) const + { + switch (biding) + { + case GL_TEXTURE_2D: + return mTextureUnits[mActiveTexture].mTexture2D; + case GL_TEXTURE_3D: + return mTextureUnits[mActiveTexture].mTexture3D; + case GL_TEXTURE_1D: + return mTextureUnits[mActiveTexture].mTexture1D; + case GL_TEXTURE_CUBE_MAP: + return mTextureUnits[mActiveTexture].mTextureCube; + case GL_TEXTURE_CUBE_MAP_ARRAY: + return mTextureUnits[mActiveTexture].mTextureCubeArray; + case GL_DRAW_FRAMEBUFFER: + return mBindedFBO_W; + case GL_READ_FRAMEBUFFER: + return mBindedFBO_R; + case GL_ARRAY_BUFFER: + return mBindedVBO; + case GL_ELEMENT_ARRAY_BUFFER: + return mBindedIBO; + default: + AssertFatal(0, avar("GFXGLStateCache::getCacheBinded - binding (%x) not supported.", biding) ); + return 0; + } + } + + /// after glActiveTexture + void setCacheActiveTexture(U32 unit) { mActiveTexture = unit; } + U32 getCacheActiveTexture() const { return mActiveTexture; } + + /// for cache glEnableVertexAttribArray / glDisableVertexAttribArray + void setCacheVertexAttribActive(U32 activeMask) { mVertexAttribActive = activeMask; } + U32 getCacheVertexAttribActive() const { return mVertexAttribActive; } + +protected: + GLuint mActiveTexture, mBindedVBO, mBindedIBO, mBindedFBO_W, mBindedFBO_R; + TextureUnit mTextureUnits[TEXTURE_STAGE_COUNT]; + U32 mVertexAttribActive; +}; + + +#endif \ No newline at end of file diff --git a/gl/gfxGLUtils.h b/gl/gfxGLUtils.h new file mode 100644 index 000000000..34eabcbd8 --- /dev/null +++ b/gl/gfxGLUtils.h @@ -0,0 +1,244 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef TORQUE_GFX_GL_GFXGLUTILS_H_ +#define TORQUE_GFX_GL_GFXGLUTILS_H_ + +#include "core/util/preprocessorHelpers.h" +#include "gfx/gl/gfxGLEnumTranslate.h" +#include "gfx/gl/gfxGLStateCache.h" +#include "gfx/bitmap/imageUtils.h" + +inline U32 getMaxMipmaps(U32 width, U32 height, U32 depth) +{ + return getMax( getBinLog2(depth), getMax(getBinLog2(width), getBinLog2(height))); +} + +inline GLenum minificationFilter(U32 minFilter, U32 mipFilter, U32 /*mipLevels*/) +{ + // the compiler should interpret this as array lookups + switch( minFilter ) + { + case GFXTextureFilterLinear: + switch( mipFilter ) + { + case GFXTextureFilterLinear: + return GL_LINEAR_MIPMAP_LINEAR; + case GFXTextureFilterPoint: + return GL_LINEAR_MIPMAP_NEAREST; + default: + return GL_LINEAR; + } + default: + switch( mipFilter ) { + case GFXTextureFilterLinear: + return GL_NEAREST_MIPMAP_LINEAR; + case GFXTextureFilterPoint: + return GL_NEAREST_MIPMAP_NEAREST; + default: + return GL_NEAREST; + } + } +} + +//Get the surface size of a compressed mip map level - see ddsLoader.cpp +inline U32 getCompressedSurfaceSize(GFXFormat format,U32 width, U32 height, U32 mipLevel=0 ) +{ + if(!ImageUtil::isCompressedFormat(format)) + return 0; + + // Bump by the mip level. + height = getMax(U32(1), height >> mipLevel); + width = getMax(U32(1), width >> mipLevel); + + U32 sizeMultiple = 0; + if(format == GFXFormatBC1 || format == GFXFormatBC1_SRGB) + sizeMultiple = 8; + else + sizeMultiple = 16; + + return getMax(U32(1), width/4) * getMax(U32(1), height/4) * sizeMultiple; +} + +/// Simple class which preserves a given GL integer. +/// This class determines the integer to preserve on construction and restores +/// it on destruction. +class GFXGLPreserveInteger +{ +public: + typedef void(STDCALL *BindFn)(GLenum, GLuint); + + /// Preserve the integer. + /// @param binding The binding which should be set on destruction. + /// @param getBinding The parameter to be passed to glGetIntegerv to determine + /// the integer to be preserved. + /// @param binder The gl function to call to restore the integer. + GFXGLPreserveInteger(GLenum binding, GLint getBinding, BindFn binder) : + mBinding(binding), mPreserved(0), mBinder(binder) + { + AssertFatal(mBinder, "GFXGLPreserveInteger - Need a valid binder function"); + mPreserved = GFXGL->getOpenglCache()->getCacheBinded(mBinding); +#if defined(TORQUE_DEBUG) && defined(TORQUE_DEBUG_GFX) + GLint bindedOnOpenglDriver; + glGetIntegerv(getBinding, &bindedOnOpenglDriver); + AssertFatal( mPreserved == bindedOnOpenglDriver, "GFXGLPreserveInteger - GFXGLDevice/OpenGL mismatch on cache binded resource."); +#endif + } + + /// Restores the integer. + ~GFXGLPreserveInteger() + { + mBinder(mBinding, mPreserved); + } + +private: + GLenum mBinding; + GLint mPreserved; + BindFn mBinder; +}; + +class GFXGLPreserveTexture +{ +public: + typedef void(STDCALL *BindFn)(GLenum, GLuint); + + GFXGLPreserveTexture(GLenum binding, GLint getBinding, BindFn binder) : + mBinding(binding), mPreserved(0), mBinder(binder) + { + AssertFatal(mBinder, "GFXGLPreserveTexture - Need a valid binder function"); + GFXGLDevice *gfx = GFXGL; + mPreserved = gfx->getOpenglCache()->getCacheBinded(mBinding); + mActiveTexture = gfx->getOpenglCache()->getCacheActiveTexture(); +#if defined(TORQUE_DEBUG) && defined(TORQUE_DEBUG_GFX) + GLint activeTextureOnOpenglDriver, bindedTextureOnOpenglDriver; + glGetIntegerv(getBinding, &bindedTextureOnOpenglDriver); + glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTextureOnOpenglDriver); + activeTextureOnOpenglDriver -= GL_TEXTURE0; + AssertFatal( mPreserved == bindedTextureOnOpenglDriver, "GFXGLPreserveTexture - GFXGLDevice/OpenGL mismatch on cache binded resource."); + AssertFatal( activeTextureOnOpenglDriver == mActiveTexture, "GFXGLPreserveTexture - GFXGLDevice/OpenGL mismatch on cache binded resource."); +#endif + } + + /// Restores the texture. + ~GFXGLPreserveTexture() + { +#if defined(TORQUE_DEBUG) && defined(TORQUE_DEBUG_GFX) + GLint activeTextureOnOpenglDriver; + glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTextureOnOpenglDriver); + activeTextureOnOpenglDriver -= GL_TEXTURE0; + GLint cacheActiveTexture = GFXGL->getOpenglCache()->getCacheActiveTexture(); + AssertFatal( cacheActiveTexture == activeTextureOnOpenglDriver, "GFXGLPreserveTexture - GFXGLDevice/OpenGL mismatch on cache ActiveTexture."); +#endif + mBinder(mBinding, mPreserved); + } + +private: + GLenum mBinding; + GLint mPreserved; + BindFn mBinder; + S16 mActiveTexture; +}; + +/// Helper macro to preserve the current VBO binding. +#define PRESERVE_VERTEX_BUFFER() \ +GFXGLPreserveInteger TORQUE_CONCAT(preserve_, __LINE__) (GL_ARRAY_BUFFER, GL_ARRAY_BUFFER_BINDING, (GFXGLPreserveInteger::BindFn)glBindBuffer) + +/// Helper macro to preserve the current element array binding. +#define PRESERVE_INDEX_BUFFER() \ +GFXGLPreserveInteger TORQUE_CONCAT(preserve_, __LINE__) (GL_ELEMENT_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER_BINDING, (GFXGLPreserveInteger::BindFn)glBindBuffer) + +#define _GET_BUFFER_BINDING( BINDING ) \ +BINDING == GL_ARRAY_BUFFER ? GL_ARRAY_BUFFER_BINDING : ( BINDING == GL_ELEMENT_ARRAY_BUFFER ? GL_ELEMENT_ARRAY_BUFFER_BINDING : 0 ) + +/// Helper macro to preserve the current element array binding. +#define PRESERVE_BUFFER( BINDING ) \ +GFXGLPreserveInteger TORQUE_CONCAT(preserve_, __LINE__) (BINDING, _GET_BUFFER_BINDING(BINDING), (GFXGLPreserveInteger::BindFn)glBindBuffer) + +/// ASSERT: Never call glActiveTexture for a "bind to modify" or in a PRESERVER_TEXTURE MACRO scope. + +/// Helper macro to preserve the current 1D texture binding. +#define PRESERVE_1D_TEXTURE() \ +GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D, (GFXGLPreserveInteger::BindFn)glBindTexture) + +/// Helper macro to preserve the current 2D texture binding. +#define PRESERVE_2D_TEXTURE() \ +GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D, (GFXGLPreserveInteger::BindFn)glBindTexture) + +/// Helper macro to preserve the current 3D texture binding. +#define PRESERVE_3D_TEXTURE() \ +GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D, (GFXGLPreserveInteger::BindFn)glBindTexture) + +/// Helper macro to preserve the current 3D texture binding. +#define PRESERVE_CUBEMAP_TEXTURE() \ +GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP, (GFXGLPreserveInteger::BindFn)glBindTexture) + +#define PRESERVE_CUBEMAP_ARRAY_TEXTURE() \ +GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_BINDING_CUBE_MAP_ARRAY, (GFXGLPreserveInteger::BindFn)glBindTexture) + +#define _GET_TEXTURE_BINDING(binding) \ +binding == GL_TEXTURE_2D ? GL_TEXTURE_BINDING_2D : (binding == GL_TEXTURE_3D ? GL_TEXTURE_BINDING_3D : GL_TEXTURE_BINDING_1D ) + +#define PRESERVE_TEXTURE(binding) \ +GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (binding, _GET_TEXTURE_BINDING(binding), (GFXGLPreserveInteger::BindFn)glBindTexture) + +#define PRESERVE_FRAMEBUFFER() \ +GFXGLPreserveInteger TORQUE_CONCAT(preserve_, __LINE__) (GL_READ_FRAMEBUFFER, GL_READ_FRAMEBUFFER_BINDING, (GFXGLPreserveInteger::BindFn)glBindFramebuffer);\ +GFXGLPreserveInteger TORQUE_CONCAT(preserve2_, __LINE__) (GL_DRAW_FRAMEBUFFER, GL_DRAW_FRAMEBUFFER_BINDING, (GFXGLPreserveInteger::BindFn)glBindFramebuffer) + + +#if TORQUE_DEBUG + + // Handy macro for checking the status of a framebuffer. Framebuffers can fail in + // all sorts of interesting ways, these are just the most common. Further, no existing GL profiling + // tool catches framebuffer errors when the framebuffer is created, so we actually need this. + #define CHECK_FRAMEBUFFER_STATUS()\ + {\ + GLenum status;\ + status = glCheckFramebufferStatus(GL_FRAMEBUFFER);\ + switch(status) {\ + case GL_FRAMEBUFFER_COMPLETE:\ + break;\ + case GL_FRAMEBUFFER_UNSUPPORTED:\ + AssertFatal(false, "Unsupported FBO");\ + break;\ + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:\ + AssertFatal(false, "Incomplete FBO Attachment");\ + break;\ + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:\ + AssertFatal(false, "Incomplete FBO Missing Attachment");\ + break;\ + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:\ + AssertFatal(false, "Incomplete FBO Draw buffer");\ + break;\ + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:\ + AssertFatal(false, "Incomplete FBO Read buffer");\ + break;\ + default:\ + /* programming error; will fail on all hardware */\ + AssertFatal(false, "Something really bad happened with an FBO");\ + }\ + } +#else + #define CHECK_FRAMEBUFFER_STATUS() +#endif //TORQUE_DEBUG + +#endif