diff --git a/Sources/Plasma/Apps/plClient/Mac-Cocoa/main.mm b/Sources/Plasma/Apps/plClient/Mac-Cocoa/main.mm index cafacfe541..beba05b48f 100644 --- a/Sources/Plasma/Apps/plClient/Mac-Cocoa/main.mm +++ b/Sources/Plasma/Apps/plClient/Mac-Cocoa/main.mm @@ -42,9 +42,6 @@ // System Frameworks #import -#ifdef PLASMA_PIPELINE_GL -#import -#endif #ifdef PLASMA_PIPELINE_METAL #import #endif diff --git a/Sources/Plasma/FeatureLib/pfDXPipeline/plDXDevice.cpp b/Sources/Plasma/FeatureLib/pfDXPipeline/plDXDevice.cpp index 0722bfbb4f..50798c0f90 100644 --- a/Sources/Plasma/FeatureLib/pfDXPipeline/plDXDevice.cpp +++ b/Sources/Plasma/FeatureLib/pfDXPipeline/plDXDevice.cpp @@ -65,6 +65,32 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #define WEAK_ERROR_CHECK( cond ) cond #endif + +/// Macros for getting/setting data in a D3D vertex buffer +template +static inline void inlCopy(uint8_t*& src, uint8_t*& dst) +{ + T* src_ptr = reinterpret_cast(src); + T* dst_ptr = reinterpret_cast(dst); + *dst_ptr = *src_ptr; + src += sizeof(T); + dst += sizeof(T); +} + +static inline void inlCopy(const uint8_t*& src, uint8_t*& dst, size_t sz) +{ + memcpy(dst, src, sz); + src += sz; + dst += sz; +} + +template +static inline void inlSkip(uint8_t*& src) +{ + src += sizeof(T) * N; +} + + static D3DMATRIX d3dIdentityMatrix{ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, @@ -72,7 +98,6 @@ static D3DMATRIX d3dIdentityMatrix{ 0.0f, 0.0f, 0.0f, 1.0f }; - D3DMATRIX& IMatrix44ToD3DMatrix(D3DMATRIX& dst, const hsMatrix44& src) { if (src.fFlags & hsMatrix44::kIsIdent) { @@ -104,11 +129,8 @@ D3DMATRIX& IMatrix44ToD3DMatrix(D3DMATRIX& dst, const hsMatrix44& src) plDXDevice::plDXDevice() -: fD3DDevice(nullptr), - fD3DMainSurface(nullptr), - fD3DDepthSurface(nullptr), - fD3DBackBuff(nullptr), - fCurrCullMode(D3DCULL_CW) +: fD3DDevice(), fD3DMainSurface(), fD3DDepthSurface(), fD3DBackBuff(), + fCurrCullMode(D3DCULL_CW), fManagedAlloced(), fAllocUnManaged() { } @@ -163,6 +185,257 @@ void plDXDevice::SetViewport() WEAK_ERROR_CHECK(fD3DDevice->SetViewport(&vp)); } +/** + * Calculate the vertex stride from the given format. + */ +static uint32_t GetBufferFormatSize(uint8_t format) +{ + uint32_t size = sizeof(float) * 6 + sizeof(uint32_t) * 2; // Position and normal, and two packed colors + + switch (format & plGBufferGroup::kSkinWeightMask) { + case plGBufferGroup::kSkinNoWeights: + break; + case plGBufferGroup::kSkin1Weight: + size += sizeof(float); + break; + default: + hsAssert(false, "Invalid skin weight value in GetBufferFormatSize()"); + } + + size += sizeof(float) * 3 * plGBufferGroup::CalcNumUVs(format); + + return size; +} + +void plDXDevice::SetupVertexBufferRef(plGBufferGroup* owner, uint32_t idx, VertexBufferRef* vRef) +{ + // Initialize to nullptr, in case something goes wrong. + vRef->fD3DBuffer = nullptr; + + uint8_t format = owner->GetVertexFormat(); + + // All indexed skinning is currently done on CPU, so the source data + // will have indices, but we strip them out for the D3D buffer. + if (format & plGBufferGroup::kSkinIndices) { + format &= ~(plGBufferGroup::kSkinWeightMask | plGBufferGroup::kSkinIndices); + format |= plGBufferGroup::kSkinNoWeights; // Should do nothing, but just in case... + vRef->SetSkinned(true); + vRef->SetVolatile(true); + } + + uint32_t vertSize = GetBufferFormatSize(format); // vertex stride + uint32_t numVerts = owner->GetVertBufferCount(idx); + + vRef->fDevice = fD3DDevice; + + vRef->fOwner = owner; + vRef->fCount = numVerts; + vRef->fVertexSize = vertSize; + vRef->fFormat = format; + vRef->fRefTime = 0; + + vRef->SetDirty(true); + vRef->SetRebuiltSinceUsed(true); + vRef->fData = nullptr; + + vRef->SetVolatile(vRef->Volatile() || owner->AreVertsVolatile()); + + vRef->fIndex = idx; + + owner->SetVertexBufferRef(idx, vRef); + hsRefCnt_SafeUnRef(vRef); +} + +void plDXDevice::CheckStaticVertexBuffer(VertexBufferRef* vRef, plGBufferGroup* owner, uint32_t idx) +{ + if (fAllocUnManaged) + return; + + hsAssert(!vRef->Volatile(), "Creating a managed vertex buffer for a volatile buffer ref"); + + if (!vRef->fD3DBuffer) { + // Okay, haven't done this one. + DWORD fvfFormat = fPipeline->IGetBufferD3DFormat(vRef->fFormat); + + D3DPOOL poolType = D3DPOOL_MANAGED; + //DWORD usage = D3DUSAGE_WRITEONLY; + DWORD usage = 0; + const int numVerts = vRef->fCount; + const int vertSize = vRef->fVertexSize; + fManagedAlloced = true; + + if (FAILED(fD3DDevice->CreateVertexBuffer(numVerts * vertSize, + usage, + fvfFormat, + poolType, + &vRef->fD3DBuffer, nullptr))) + { + hsAssert(false, "CreateVertexBuffer() call failed!"); + vRef->fD3DBuffer = nullptr; + return; + } + PROFILE_POOL_MEM(poolType, numVerts * vertSize, true, ST_LITERAL("VtxBuff")); + + // Fill in the vertex data. + FillStaticVertexBufferRef(vRef, owner, idx); + + // This is currently a no op, but this would let the buffer know it can + // unload the system memory copy, since we have a managed version now. + owner->PurgeVertBuffer(idx); + } +} + +void plDXDevice::FillStaticVertexBufferRef(VertexBufferRef* ref, plGBufferGroup* group, uint32_t idx) +{ + IDirect3DVertexBuffer9* vertexBuff = ref->fD3DBuffer; + + if (!vertexBuff) + // We most likely already warned about this earlier, best to just quietly return now + return; + + const uint32_t vertSize = ref->fVertexSize; + const uint32_t vertStart = group->GetVertBufferStart(idx) * vertSize; + const uint32_t size = group->GetVertBufferEnd(idx) * vertSize - vertStart; + if (!size) + return; + + /// Lock the buffer + uint8_t* ptr; + if (FAILED(vertexBuff->Lock(vertStart, size, (void **)&ptr, group->AreVertsVolatile() ? D3DLOCK_DISCARD : 0))) + hsAssert(false, "Failed to lock vertex buffer for writing"); + + if (ref->fData) { + memcpy(ptr, ref->fData + vertStart, size); + } else { + hsAssert(0 == vertStart, "Offsets on non-interleaved data not supported"); + hsAssert(group->GetVertBufferCount(idx) * vertSize == size, "Trailing dead space on non-interleaved data not supported"); + + const uint32_t vertSmallSize = group->GetVertexLiteStride() - sizeof(hsPoint3) * 2; + uint8_t* srcVPtr = group->GetVertBufferData(idx); + plGBufferColor* const srcCPtr = group->GetColorBufferData(idx); + + const int numCells = group->GetNumCells(idx); + for (int i = 0; i < numCells; i++) { + plGBufferCell *cell = group->GetCell(idx, i); + + if (cell->fColorStart == uint32_t(-1)) { + /// Interleaved, do straight copy + memcpy(ptr, srcVPtr + cell->fVtxStart, cell->fLength * vertSize); + ptr += cell->fLength * vertSize; + } else { + /// Separated, gotta interleave + uint8_t* tempVPtr = srcVPtr + cell->fVtxStart; + plGBufferColor* tempCPtr = srcCPtr + cell->fColorStart; + + for (int j = 0; j < cell->fLength; j++) { + memcpy(ptr, tempVPtr, sizeof(hsPoint3) * 2); + ptr += sizeof(hsPoint3) * 2; + tempVPtr += sizeof(hsPoint3) * 2; + + memcpy(ptr, &tempCPtr->fDiffuse, sizeof(uint32_t)); + ptr += sizeof(uint32_t); + memcpy(ptr, &tempCPtr->fSpecular, sizeof(uint32_t)); + ptr += sizeof(uint32_t); + + memcpy(ptr, tempVPtr, vertSmallSize); + ptr += vertSmallSize; + tempVPtr += vertSmallSize; + tempCPtr++; + } + } + } + } + + /// Unlock and clean up + vertexBuff->Unlock(); + ref->SetRebuiltSinceUsed(true); + ref->SetDirty(false); +} + +void plDXDevice::FillVolatileVertexBufferRef(VertexBufferRef* ref, plGBufferGroup* group, uint32_t idx) +{ + uint8_t* dst = ref->fData; + uint8_t* src = group->GetVertBufferData(idx); + + size_t uvChanSize = plGBufferGroup::CalcNumUVs(group->GetVertexFormat()) * sizeof(float) * 3; + uint8_t numWeights = (group->GetVertexFormat() & plGBufferGroup::kSkinWeightMask) >> 4; + + for (uint32_t i = 0; i < ref->fCount; ++i) { + inlCopy(src, dst); // pre-pos + src += numWeights * sizeof(float); // weights + if (group->GetVertexFormat() & plGBufferGroup::kSkinIndices) + inlSkip(src); // indices + inlCopy(src, dst); // pre-normal + inlCopy(src, dst); // diffuse + inlCopy(src, dst); // specular + + // UVWs + memcpy(dst, src, uvChanSize); + src += uvChanSize; + dst += uvChanSize; + } +} + +void plDXDevice::SetupIndexBufferRef(plGBufferGroup* owner, uint32_t idx, IndexBufferRef* iRef) +{ + uint32_t numIndices = owner->GetIndexBufferCount(idx); + iRef->fCount = numIndices; + iRef->fOwner = owner; + iRef->fIndex = idx; + iRef->fRefTime = 0; + + iRef->SetDirty(true); + iRef->SetRebuiltSinceUsed(true); + + owner->SetIndexBufferRef(idx, iRef); + hsRefCnt_SafeUnRef(iRef); + + iRef->SetVolatile(owner->AreIdxVolatile()); +} + +void plDXDevice::CheckIndexBuffer(IndexBufferRef* iRef) +{ + if (!iRef->fD3DBuffer && iRef->fCount) { + D3DPOOL poolType = fAllocUnManaged ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; + DWORD usage = D3DUSAGE_WRITEONLY; + iRef->SetVolatile(false); + if (FAILED(fD3DDevice->CreateIndexBuffer(sizeof(uint16_t) * iRef->fCount, + usage, + D3DFMT_INDEX16, + poolType, + &iRef->fD3DBuffer, nullptr))) + { + hsAssert(false, "CreateIndexBuffer() call failed!"); + iRef->fD3DBuffer = nullptr; + return; + } + PROFILE_POOL_MEM(poolType, sizeof(uint16_t) * iRef->fCount, true, ST_LITERAL("IndexBuff")); + + iRef->fPoolType = poolType; + iRef->SetDirty(true); + iRef->SetRebuiltSinceUsed(true); + } +} + +void plDXDevice::FillIndexBufferRef(IndexBufferRef* iRef, plGBufferGroup* owner, uint32_t idx) +{ + uint32_t startIdx = owner->GetIndexBufferStart(idx); + uint32_t size = (owner->GetIndexBufferEnd(idx) - startIdx) * sizeof(uint16_t); + if (!size) + return; + + DWORD lockFlags = iRef->Volatile() ? D3DLOCK_DISCARD : 0; + uint16_t* destPtr = nullptr; + if (FAILED(iRef->fD3DBuffer->Lock(startIdx * sizeof(uint16_t), size, (void **)&destPtr, lockFlags))) { + hsAssert(false, "Cannot lock index buffer for writing"); + return; + } + + memcpy(destPtr, owner->GetIndexBufferData(idx) + startIdx, size); + + iRef->fD3DBuffer->Unlock(); + iRef->SetDirty(false); +} void plDXDevice::SetProjectionMatrix(const hsMatrix44& src) { @@ -195,3 +468,20 @@ ST::string plDXDevice::GetErrorString() const { return fPipeline->fSettings.fErrorStr; } + +void plDXDevice::BeginAllocUnManaged() +{ + // Flush out all managed resources to make room for unmanaged resources. + fD3DDevice->EvictManagedResources(); + + fManagedAlloced = false; + fAllocUnManaged = true; // we're currently only allocating POOL_DEFAULT +} + +void plDXDevice::EndAllocUnManaged() +{ + fAllocUnManaged = false; + + // Flush the (should be empty) resource manager to reset its internal allocation pool. + fD3DDevice->EvictManagedResources(); +} diff --git a/Sources/Plasma/FeatureLib/pfDXPipeline/plDXDevice.h b/Sources/Plasma/FeatureLib/pfDXPipeline/plDXDevice.h index f34354760b..68c86364ac 100644 --- a/Sources/Plasma/FeatureLib/pfDXPipeline/plDXDevice.h +++ b/Sources/Plasma/FeatureLib/pfDXPipeline/plDXDevice.h @@ -50,7 +50,9 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "hsWindows.h" #include +class plCubicEnvironmap; class plDXPipeline; +class plGBufferGroup; class plRenderTarget; struct IDirect3DDevice9; struct IDirect3DSurface9; @@ -77,6 +79,9 @@ class plDXDevice D3DCULL fCurrCullMode; + bool fManagedAlloced; + bool fAllocUnManaged; + public: plDXDevice(); @@ -94,12 +99,41 @@ class plDXDevice /** Translate our viewport into a D3D viewport. */ void SetViewport(); + /* Device Ref Functions **************************************************/ + void SetupVertexBufferRef(plGBufferGroup* owner, uint32_t idx, VertexBufferRef* vRef); + void CheckStaticVertexBuffer(VertexBufferRef* vRef, plGBufferGroup* owner, uint32_t idx); + void FillStaticVertexBufferRef(VertexBufferRef* ref, plGBufferGroup* group, uint32_t idx); + void FillVolatileVertexBufferRef(VertexBufferRef* ref, plGBufferGroup* group, uint32_t idx); + + void SetupIndexBufferRef(plGBufferGroup* owner, uint32_t idx, IndexBufferRef* iRef); + void CheckIndexBuffer(IndexBufferRef* iRef); + void FillIndexBufferRef(IndexBufferRef* iRef, plGBufferGroup* owner, uint32_t idx); + + // Texture stuff is all handled in plDXPipeline, not in plDXDevice + void SetupTextureRef(plLayerInterface* layer, plBitmap* img, TextureRef* tRef) {} + void CheckTexture(TextureRef* tRef) {} + void MakeTextureRef(TextureRef* tRef, plLayerInterface* layer, plMipmap* img) {} + void MakeCubicTextureRef(TextureRef* tRef, plLayerInterface* layer, plCubicEnvironmap* img) {} void SetProjectionMatrix(const hsMatrix44& src); void SetWorldToCameraMatrix(const hsMatrix44& src); void SetLocalToWorldMatrix(const hsMatrix44& src); ST::string GetErrorString() const; + + /** + * Before allocating anything into POOL_DEFAULT, we must evict managed memory. + * + * See plDXPipeline::LoadResources. + */ + void BeginAllocUnManaged(); + + /** + * Before allocating anything into POOL_DEFAULT, we must evict managed memory. + * + * See plDXPipeline::LoadResources. + */ + void EndAllocUnManaged(); }; #endif diff --git a/Sources/Plasma/FeatureLib/pfDXPipeline/plDXDeviceRefs.cpp b/Sources/Plasma/FeatureLib/pfDXPipeline/plDXDeviceRefs.cpp index a0b7606ba7..f5f3ead9b9 100644 --- a/Sources/Plasma/FeatureLib/pfDXPipeline/plDXDeviceRefs.cpp +++ b/Sources/Plasma/FeatureLib/pfDXPipeline/plDXDeviceRefs.cpp @@ -75,9 +75,9 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plProfile.h" #include "plStatusLog/plStatusLog.h" -plProfile_CreateMemCounter("Vertices", "Memory", MemVertex); -plProfile_CreateMemCounter("Indices", "Memory", MemIndex); -plProfile_CreateMemCounter("Textures", "Memory", MemTexture); +plProfile_Extern(MemVertex); +plProfile_Extern(MemIndex); +plProfile_Extern(MemTexture); /////////////////////////////////////////////////////////////////////////////// //// Generic plDXDeviceRef Functions ///////////////////////////////////////// diff --git a/Sources/Plasma/FeatureLib/pfDXPipeline/plDXPipeline.cpp b/Sources/Plasma/FeatureLib/pfDXPipeline/plDXPipeline.cpp index ded0d4ffa9..02fa842066 100644 --- a/Sources/Plasma/FeatureLib/pfDXPipeline/plDXPipeline.cpp +++ b/Sources/Plasma/FeatureLib/pfDXPipeline/plDXPipeline.cpp @@ -278,34 +278,39 @@ const int kMaxProjectors = 100; plProfile_CreateMemCounter("Pipeline Surfaces", "Memory", MemPipelineSurfaces); plProfile_Extern(MemVertex); plProfile_Extern(MemIndex); -plProfile_CreateCounter("Feed Triangles", "Draw", DrawFeedTriangles); -plProfile_CreateCounter("Draw Prim Static", "Draw", DrawPrimStatic); -plProfile_CreateMemCounter("Total Texture Size", "Draw", TotalTexSize); -plProfile_CreateCounter("Layer Change", "Draw", LayChange); +plProfile_Extern(DrawFeedTriangles); +plProfile_Extern(DrawPrimStatic); +plProfile_Extern(TotalTexSize); +plProfile_Extern(LayChange); plProfile_Extern(DrawTriangles); plProfile_Extern(MatChange); - -plProfile_CreateCounterNoReset("Reload", "PipeC", PipeReload); - -plProfile_CreateTimer("PrepShadows", "PipeT", PrepShadows); -plProfile_CreateTimer("PrepDrawable", "PipeT", PrepDrawable); -plProfile_CreateTimer(" Skin", "PipeT", Skin); -plProfile_CreateTimer(" AvSort", "PipeT", AvatarSort); -plProfile_CreateTimer(" ClearLights", "PipeT", ClearLights); -plProfile_CreateTimer("RenderSpan", "PipeT", RenderSpan); -plProfile_CreateTimer(" MergeCheck", "PipeT", MergeCheck); -plProfile_CreateTimer(" MergeSpan", "PipeT", MergeSpan); -plProfile_CreateTimer(" SpanTransforms", "PipeT", SpanTransforms); -plProfile_CreateTimer(" SpanFog", "PipeT", SpanFog); -plProfile_CreateTimer(" SelectLights", "PipeT", SelectLights); -plProfile_CreateTimer(" SelectProj", "PipeT", SelectProj); -plProfile_CreateTimer(" CheckDyn", "PipeT", CheckDyn); -plProfile_CreateTimer(" CheckStat", "PipeT", CheckStat); -plProfile_CreateTimer(" RenderBuff", "PipeT", RenderBuff); -plProfile_CreateTimer(" RenderPrim", "PipeT", RenderPrim); -plProfile_CreateTimer("PlateMgr", "PipeT", PlateMgr); -plProfile_CreateTimer("DebugText", "PipeT", DebugText); -plProfile_CreateTimer("Reset", "PipeT", Reset); +plProfile_Extern(NumSkin); +plProfile_Extern(PipeReload); +plProfile_Extern(PrepShadows); +plProfile_Extern(PrepDrawable); +plProfile_Extern(Skin); +plProfile_Extern(ClearLights); +plProfile_Extern(RenderSpan); +plProfile_Extern(MergeCheck); +plProfile_Extern(MergeSpan); +plProfile_Extern(SpanTransforms); +plProfile_Extern(SpanFog); +plProfile_Extern(SelectLights); +plProfile_Extern(SelectProj); +plProfile_Extern(CheckDyn); +plProfile_Extern(CheckStat); +plProfile_Extern(RenderBuff); +plProfile_Extern(RenderPrim); +plProfile_Extern(PlateMgr); +plProfile_Extern(DebugText); +plProfile_Extern(Reset); +plProfile_Extern(AvRTPoolUsed); +plProfile_Extern(AvRTPoolCount); +plProfile_Extern(AvRTPoolRes); +plProfile_Extern(AvRTShrinkTime); +plProfile_Extern(SpanMerge); +plProfile_Extern(MatLightState); +plProfile_Extern(EmptyList); plProfile_CreateMemCounter("DefMem", "PipeC", DefaultMem); plProfile_CreateMemCounter("ManMem", "PipeC", ManagedMem); @@ -315,19 +320,10 @@ plProfile_CreateMemCounterReset("fTexUsed", "PipeC", fTexUsed); plProfile_CreateMemCounterReset("fTexManaged", "PipeC", fTexManaged); plProfile_CreateMemCounterReset("fVtxUsed", "PipeC", fVtxUsed); plProfile_CreateMemCounterReset("fVtxManaged", "PipeC", fVtxManaged); -plProfile_CreateCounter("Merge", "PipeC", SpanMerge); plProfile_CreateCounter("TexNum", "PipeC", NumTex); -plProfile_CreateCounter("LiState", "PipeC", MatLightState); -plProfile_CreateCounter("NumSkin", "PipeC", NumSkin); -plProfile_CreateCounter("AvatarFaces", "PipeC", AvatarFaces); plProfile_CreateCounter("VertexChange", "PipeC", VertexChange); plProfile_CreateCounter("IndexChange", "PipeC", IndexChange); plProfile_CreateCounter("DynVBuffs", "PipeC", DynVBuffs); -plProfile_CreateCounter("EmptyList", "PipeC", EmptyList); -plProfile_CreateCounter("AvRTPoolUsed", "PipeC", AvRTPoolUsed); -plProfile_CreateCounter("AvRTPoolCount", "PipeC", AvRTPoolCount); -plProfile_CreateCounter("AvRTPoolRes", "PipeC", AvRTPoolRes); -plProfile_CreateCounter("AvRTShrinkTime", "PipeC", AvRTShrinkTime); #ifndef PLASMA_EXTERNAL_RELEASE /// Fun inlines for keeping track of surface creation/deletion memory @@ -478,43 +474,24 @@ void plDXPipeline::ProfilePoolMem(D3DPOOL poolType, uint32_t size, bool add, con // First, Declarations. // Adding a nil RenderPrim for turning off drawing -class plRenderNilFunc : public plRenderPrimFunc -{ -public: - plRenderNilFunc() {} - - bool RenderPrims() const override { return false; } -}; static plRenderNilFunc sRenderNil; -class plRenderTriListFunc : public plRenderPrimFunc +class plDXRenderTriListFunc : public plRenderTriListFunc { -protected: - LPDIRECT3DDEVICE9 fD3DDevice; - int fBaseVertexIndex; - int fVStart; - int fVLength; - int fIStart; - int fNumTris; public: - plRenderTriListFunc(LPDIRECT3DDEVICE9 d3dDevice, int baseVertexIndex, + plDXRenderTriListFunc(LPDIRECT3DDEVICE9 d3dDevice, int baseVertexIndex, int vStart, int vLength, int iStart, int iNumTris) - : fD3DDevice(d3dDevice), fBaseVertexIndex(baseVertexIndex), fVStart(vStart), fVLength(vLength), fIStart(iStart), fNumTris(iNumTris) {} + : plRenderTriListFunc(d3dDevice, baseVertexIndex, vStart, vLength, iStart, iNumTris) {} - bool RenderPrims() const override; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Implementations - -bool plRenderTriListFunc::RenderPrims() const -{ - plProfile_IncCount(DrawFeedTriangles, fNumTris); - plProfile_IncCount(DrawTriangles, fNumTris); - plProfile_Inc(DrawPrimStatic); + bool RenderPrims() const override + { + plProfile_IncCount(DrawFeedTriangles, fNumTris); + plProfile_IncCount(DrawTriangles, fNumTris); + plProfile_Inc(DrawPrimStatic); - return FAILED( fD3DDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, fBaseVertexIndex, fVStart, fVLength, fIStart, fNumTris ) ); -} + return FAILED( fDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, fBaseVertexIndex, fVStart, fVLength, fIStart, fNumTris ) ); + } +}; //// Constructor & Destructor ///////////////////////////////////////////////// @@ -524,9 +501,7 @@ uint32_t plDXPipeline::fVtxUsed(0); uint32_t plDXPipeline::fVtxManaged(0); plDXPipeline::plDXPipeline( hsWinRef hWnd, const hsG3DDeviceModeRecord *devModeRec ) -: pl3DPipeline(devModeRec), - fManagedAlloced(false), - fAllocUnManaged(false) +: pl3DPipeline(devModeRec) { hsAssert(D3DTSS_TCI_PASSTHRU == plLayerInterface::kUVWPassThru, "D3D Enum has changed. Notify graphics department."); hsAssert(D3DTSS_TCI_CAMERASPACENORMAL == plLayerInterface::kUVWNormal, "D3D Enum has changed. Notify graphics department."); @@ -1732,8 +1707,8 @@ void plDXPipeline::IReleaseDeviceObjects() fD3DDevice = nullptr; } - fManagedAlloced = false; - fAllocUnManaged = false; + fDevice.fManagedAlloced = false; + fDevice.fAllocUnManaged = false; } // IReleaseDynamicBuffers ///////////////////////////////////////////////// @@ -1826,7 +1801,7 @@ void plDXPipeline::ICreateDynamicBuffers() fVtxRefTime++; DWORD usage = D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC; - hsAssert(!fManagedAlloced, "Alloc default with managed alloc'd"); + hsAssert(!fDevice.fManagedAlloced, "Alloc default with managed alloc'd"); D3DPOOL poolType = D3DPOOL_DEFAULT; if( fDynVtxSize ) { @@ -1990,7 +1965,7 @@ bool plDXPipeline::IResetDevice() } fSettings.fCurrFVFFormat = 0; fSettings.fCurrVertexShader = nullptr; - fManagedAlloced = false; + fDevice.fManagedAlloced = false; ICreateDynDeviceObjects(); IInitDeviceState(); @@ -2206,7 +2181,7 @@ void plDXPipeline::Resize( uint32_t width, uint32_t height ) IReleaseDynDeviceObjects(); HRESULT hr = fD3DDevice->Reset(&fSettings.fPresentParams); - fManagedAlloced = false; + fDevice.fManagedAlloced = false; if( !FAILED(hr) ) { ICreateDynDeviceObjects(); @@ -2352,153 +2327,6 @@ bool plDXPipeline::PreRender(plDrawable* drawable, std::vector& visLis return !visList.empty(); } -struct plSortFace -{ - uint16_t fIdx[3]; - float fDist; -}; - -struct plCompSortFace -{ - bool operator()( const plSortFace& lhs, const plSortFace& rhs) const - { - return lhs.fDist > rhs.fDist; - } -}; - -// IAvatarSort ///////////////////////////////////////////////////////////////////////// -// We handle avatar sort differently from the rest of the face sort. The reason is that -// within the single avatar index buffer, we want to only sort the faces of spans requesting -// a sort, and sort them in place. -// Contrast that with the normal scene translucency sort. There, we sort all the spans in a drawble, -// then we sort all the faces in that drawable, then for each span in the sorted span list, we extract -// the faces for that span appending onto the index buffer. This gives great efficiency because -// only the visible faces are sorted and they wind up packed into the front of the index buffer, which -// permits more batching. See plDrawableSpans::SortVisibleSpans. -// For the avatar, it's generally the case that all the avatar is visible or not, and there is only -// one material, so neither of those efficiencies is helpful. Moreover, for the avatar the faces we -// want sorted are a tiny subset of the avatar's faces. Moreover, and most importantly, for the avatar, we -// want to preserve the order that spans are drawn, so, for example, the opaque base head will always be -// drawn before the translucent hair fringe, which will always be drawn before the pink clear plastic baseball cap. -bool plDXPipeline::IAvatarSort(plDrawableSpans* d, const std::vector& visList) -{ - plProfile_BeginTiming(AvatarSort); - for (int16_t visIdx : visList) - { - hsAssert(d->GetSpan(visIdx)->fTypeMask & plSpan::kIcicleSpan, "Unknown type for sorting faces"); - - plIcicle* span = (plIcicle*)d->GetSpan(visIdx); - - if( span->fProps & plSpan::kPartialSort ) - { - hsAssert(d->GetBufferGroup(span->fGroupIdx)->AreIdxVolatile(), "Badly setup buffer group - set PartialSort too late?"); - - const hsPoint3 viewPos = GetViewPositionWorld(); - - plGBufferGroup* group = d->GetBufferGroup(span->fGroupIdx); - - plDXVertexBufferRef* vRef = (plDXVertexBufferRef*)group->GetVertexBufferRef(span->fVBufferIdx); - - const uint8_t* vdata = vRef->fData; - const uint32_t stride = vRef->fVertexSize; - - const int numTris = span->fILength/3; - - static std::vector sortScratch; - sortScratch.resize(numTris); - - plProfile_IncCount(AvatarFaces, numTris); - - // - // Have three very similar sorts here, differing only on where the "position" of - // each triangle is defined, either as the center of the triangle, the nearest - // point on the triangle, or the farthest point on the triangle. - // Having tried all three on the avatar (the only thing this sort is used on), - // the best results surprisingly came from using the center of the triangle. - uint16_t* indices = group->GetIndexBufferData(span->fIBufferIdx) + span->fIStartIdx; - int j; - for( j = 0; j < numTris; j++ ) - { -#if 1 // TRICENTER - uint16_t idx = *indices++; - sortScratch[j].fIdx[0] = idx; - hsPoint3 pos = *(hsPoint3*)(vdata + idx * stride); - - idx = *indices++; - sortScratch[j].fIdx[1] = idx; - pos += *(hsPoint3*)(vdata + idx * stride); - - idx = *indices++; - sortScratch[j].fIdx[2] = idx; - pos += *(hsPoint3*)(vdata + idx * stride); - - pos *= 0.3333f; - - sortScratch[j].fDist = hsVector3(&pos, &viewPos).MagnitudeSquared(); -#elif 0 // NEAREST - uint16_t idx = *indices++; - sortScratch[j].fIdx[0] = idx; - hsPoint3 pos = *(hsPoint3*)(vdata + idx * stride); - float dist = hsVector3(&pos, &viewPos).MagnitudeSquared(); - float minDist = dist; - - idx = *indices++; - sortScratch[j].fIdx[1] = idx; - pos = *(hsPoint3*)(vdata + idx * stride); - dist = hsVector3(&pos, &viewPos).MagnitudeSquared(); - if( dist < minDist ) - minDist = dist; - - idx = *indices++; - sortScratch[j].fIdx[2] = idx; - pos = *(hsPoint3*)(vdata + idx * stride); - dist = hsVector3(&pos, &viewPos).MagnitudeSquared(); - if( dist < minDist ) - minDist = dist; - - sortScratch[j].fDist = minDist; -#elif 1 // FURTHEST - uint16_t idx = *indices++; - sortScratch[j].fIdx[0] = idx; - hsPoint3 pos = *(hsPoint3*)(vdata + idx * stride); - float dist = hsVector3(&pos, &viewPos).MagnitudeSquared(); - float maxDist = dist; - - idx = *indices++; - sortScratch[j].fIdx[1] = idx; - pos = *(hsPoint3*)(vdata + idx * stride); - dist = hsVector3(&pos, &viewPos).MagnitudeSquared(); - if( dist > maxDist ) - maxDist = dist; - - idx = *indices++; - sortScratch[j].fIdx[2] = idx; - pos = *(hsPoint3*)(vdata + idx * stride); - dist = hsVector3(&pos, &viewPos).MagnitudeSquared(); - if( dist > maxDist ) - maxDist = dist; - - sortScratch[j].fDist = maxDist; -#endif // SORTTYPES - } - - std::sort(sortScratch.begin(), sortScratch.end(), plCompSortFace()); - - indices = group->GetIndexBufferData(span->fIBufferIdx) + span->fIStartIdx; - for (const plSortFace& iter : sortScratch) - { - *indices++ = iter.fIdx[0]; - *indices++ = iter.fIdx[1]; - *indices++ = iter.fIdx[2]; - } - - group->DirtyIndexBuffer(span->fIBufferIdx); - } - } - plProfile_EndTiming(AvatarSort); - return true; -} - // PrepForRender ////////////////////////////////////////////////////////////////// // Make sure the given drawable and each of the spans to be drawn (as noted in the // indices in visList) is ready to be rendered. @@ -2748,7 +2576,7 @@ bool plDXPipeline::ICheckDynBuffers(plDrawableSpans* drawable, plGBufferGroup* g } if( iRef->IsDirty() ) { - IFillIndexBufferRef(iRef, group, span->fIBufferIdx); + fDevice.FillIndexBufferRef(iRef, group, span->fIBufferIdx); iRef->SetRebuiltSinceUsed(true); } @@ -3482,7 +3310,7 @@ hsGDeviceRef *plDXPipeline::MakeRenderTargetRef( plRenderTarget *owner ) D3DRESOURCETYPE resType; plCubicRenderTarget *cubicRT; - hsAssert(!fManagedAlloced, "Allocating non-managed resource with managed resources alloc'd"); + hsAssert(!fDevice.fManagedAlloced, "Allocating non-managed resource with managed resources alloc'd"); // If we have Shader Model 3 and support non-POT textures, let's make reflections the pipe size plDynamicCamMap* camMap = plDynamicCamMap::ConvertNoRef(owner); @@ -3717,7 +3545,7 @@ hsGDeviceRef* plDXPipeline::SharedRenderTargetRef(plRenderTarget* share, plRende if( !share ) return MakeRenderTargetRef(owner); - hsAssert(!fManagedAlloced, "Allocating non-managed resource with managed resources alloc'd"); + hsAssert(!fDevice.fManagedAlloced, "Allocating non-managed resource with managed resources alloc'd"); #ifdef HS_DEBUGGING // Check out the validity of the match. Debug only. @@ -3767,7 +3595,7 @@ hsGDeviceRef* plDXPipeline::SharedRenderTargetRef(plRenderTarget* share, plRende else ref = new plDXRenderTargetRef( surfFormat, 0, owner ); - hsAssert(!fManagedAlloced, "Alloc default with managed alloc'd"); + hsAssert(!fDevice.fManagedAlloced, "Alloc default with managed alloc'd"); if( !FAILED( fD3DDevice->CreateCubeTexture( owner->GetWidth(), 1, D3DUSAGE_RENDERTARGET, surfFormat, D3DPOOL_DEFAULT, (IDirect3DCubeTexture9 **)&cTexture, nullptr))) { @@ -3814,7 +3642,7 @@ hsGDeviceRef* plDXPipeline::SharedRenderTargetRef(plRenderTarget* share, plRende else ref = new plDXRenderTargetRef( surfFormat, 0, owner ); - hsAssert(!fManagedAlloced, "Alloc default with managed alloc'd"); + hsAssert(!fDevice.fManagedAlloced, "Alloc default with managed alloc'd"); if( !FAILED( fD3DDevice->CreateTexture( owner->GetWidth(), owner->GetHeight(), 1, D3DUSAGE_RENDERTARGET, surfFormat, D3DPOOL_DEFAULT, (IDirect3DTexture9 **)&texture, nullptr))) { @@ -5320,88 +5148,6 @@ void plDXPipeline::IBottomLayer() // Special effects ///////////////////////////////////////////////////////////// -// IPushOverBaseLayer ///////////////////////////////////////////////////////// -// Sets fOverBaseLayer (if any) as a wrapper on top of input layer. -// This allows the OverBaseLayer to intercept and modify queries of -// the real current layer's properties (e.g. color or state). -// fOverBaseLayer is set to only get applied to the base layer during -// multitexturing. -// Must be matched with call to IPopOverBaseLayer. -plLayerInterface* plDXPipeline::IPushOverBaseLayer(plLayerInterface* li) -{ - if( !li ) - return nullptr; - - fOverLayerStack.push_back(li); - - if( !fOverBaseLayer ) - return fOverBaseLayer = li; - - fForceMatHandle = true; - fOverBaseLayer = fOverBaseLayer->Attach(li); - fOverBaseLayer->Eval(fTime, fFrame, 0); - return fOverBaseLayer; -} - -// IPopOverBaseLayer ///////////////////////////////////////////////////////// -// Removes fOverBaseLayer as wrapper on top of input layer. -// Should match calls to IPushOverBaseLayer. -plLayerInterface* plDXPipeline::IPopOverBaseLayer(plLayerInterface* li) -{ - if( !li ) - return nullptr; - - fForceMatHandle = true; - - plLayerInterface* pop = fOverLayerStack.back(); - fOverLayerStack.pop_back(); - fOverBaseLayer = fOverBaseLayer->Detach(pop); - - return pop; -} - -// IPushOverAllLayer /////////////////////////////////////////////////// -// Push fOverAllLayer (if any) as wrapper around the input layer. -// fOverAllLayer is set to be applied to each layer during multitexturing. -// Must be matched by call to IPopOverAllLayer -plLayerInterface* plDXPipeline::IPushOverAllLayer(plLayerInterface* li) -{ - if( !li ) - return nullptr; - - fOverLayerStack.push_back(li); - - if( !fOverAllLayer ) - { - fOverAllLayer = li; - fOverAllLayer->Eval(fTime, fFrame, 0); - return fOverAllLayer; - } - - fForceMatHandle = true; - fOverAllLayer = fOverAllLayer->Attach(li); - fOverAllLayer->Eval(fTime, fFrame, 0); - - return fOverAllLayer; -} - -// IPopOverAllLayer ////////////////////////////////////////////////// -// Remove fOverAllLayer as wrapper on top of input layer. -// Should match calls to IPushOverAllLayer. -plLayerInterface* plDXPipeline::IPopOverAllLayer(plLayerInterface* li) -{ - if( !li ) - return nullptr; - - fForceMatHandle = true; - - plLayerInterface* pop = fOverLayerStack.back(); - fOverLayerStack.pop_back(); - fOverAllLayer = fOverAllLayer->Detach(pop); - - return pop; -} - // PiggyBacks - used in techniques like projective lighting. // PiggyBacks are layers appended to each drawprimitive pass. // For example, if a material has 3 layers which will be drawn @@ -5890,22 +5636,18 @@ void plDXPipeline::IHandleTextureStage( uint32_t stage, plLayerInterface *lay void plDXPipeline::CheckTextureRef(plLayerInterface* layer) { plBitmap* bitmap = layer->GetTexture(); - if( bitmap ) - { + if (bitmap) { hsGDeviceRef* ref = bitmap->GetDeviceRef(); - if( !ref ) - { + if (!ref) { plMipmap* mip = plMipmap::ConvertNoRef(bitmap); - if( mip ) - { + if (mip) { MakeTextureRef(layer, mip); return; } plCubicEnvironmap* cubic = plCubicEnvironmap::ConvertNoRef(bitmap); - if( cubic ) - { + if (cubic) { IMakeCubicTextureRef(layer, cubic); return; } @@ -7268,7 +7010,7 @@ IDirect3DTexture9 *plDXPipeline::IMakeD3DTexture( plDXTextureRef *ref, D3DFORM { D3DPOOL poolType = D3DPOOL_MANAGED; IDirect3DTexture9 *texPtr; - fManagedAlloced = true; + fDevice.fManagedAlloced = true; if( FAILED( fSettings.fDXError = fD3DDevice->CreateTexture( ref->fMaxWidth, ref->fMaxHeight, ref->fMMLvs, IGetD3DTextureUsage(ref), @@ -7335,7 +7077,7 @@ IDirect3DCubeTexture9 *plDXPipeline::IMakeD3DCubeTexture( plDXTextureRef *ref, { D3DPOOL poolType = D3DPOOL_MANAGED; IDirect3DCubeTexture9 *texPtr = nullptr; - fManagedAlloced = true; + fDevice.fManagedAlloced = true; WEAK_ERROR_CHECK(fD3DDevice->CreateCubeTexture( ref->fMaxWidth, ref->fMMLvs, 0, formatType, poolType, &texPtr, nullptr)); PROFILE_POOL_MEM(poolType, ref->fDataSize, true, ref->fOwner && ref->fOwner->GetKey() ? ref->fOwner->GetKey()->GetUoid().GetObjectName() : ST_LITERAL("(UnknownTexture)")); fTexManaged += ref->fDataSize; @@ -7997,20 +7739,6 @@ void plDXPipeline::IFormatTextureData( uint32_t formatType, uint32_t numPix, } -/////////////////////////////////////////////////////////////////////////////// -//// View Stuff /////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// - -//// IIsViewLeftHanded //////////////////////////////////////////////////////// -// Returns true if the combination of the local2world and world2camera -// matrices is left-handed. - -bool plDXPipeline::IIsViewLeftHanded() -{ - return fView.GetViewTransform().GetOrthogonal() ^ ( fView.fLocalToWorldLeftHanded ^ fView.fWorldToCamLeftHanded ) ? true : false; -} - - /////////////////////////////////////////////////////////////////////////////// //// Transforms /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -8056,7 +7784,7 @@ void plDXPipeline::ISetCullMode(bool flip) D3DCULL newCull = D3DCULL_NONE; if( !(fLayerState[0].fMiscFlags & hsGMatState::kMiscTwoSided) ) - newCull = !IIsViewLeftHanded() ^ !flip ? D3DCULL_CW : D3DCULL_CCW; + newCull = !fView.IsViewLeftHanded() ^ !flip ? D3DCULL_CW : D3DCULL_CCW; if( newCull != fDevice.fCurrCullMode ) { @@ -8065,200 +7793,6 @@ void plDXPipeline::ISetCullMode(bool flip) } } -// ISetupVertexBufferRef ///////////////////////////////////////////////////////// -// Initialize input vertex buffer ref according to source. -void plDXPipeline::ISetupVertexBufferRef(plGBufferGroup* owner, uint32_t idx, plDXVertexBufferRef* vRef) -{ - // Initialize to nullptr, in case something goes wrong. - vRef->fD3DBuffer = nullptr; - - uint8_t format = owner->GetVertexFormat(); - - // All indexed skinning is currently done on CPU, so the source data - // will have indices, but we strip them out for the D3D buffer. - if( format & plGBufferGroup::kSkinIndices ) - { - format &= ~(plGBufferGroup::kSkinWeightMask | plGBufferGroup::kSkinIndices); - format |= plGBufferGroup::kSkinNoWeights; // Should do nothing, but just in case... - vRef->SetSkinned(true); - vRef->SetVolatile(true); - } - - uint32_t vertSize = IGetBufferFormatSize(format); // vertex stride - uint32_t numVerts = owner->GetVertBufferCount(idx); - - vRef->fDevice = fD3DDevice; - - vRef->fOwner = owner; - vRef->fCount = numVerts; - vRef->fVertexSize = vertSize; - vRef->fFormat = format; - vRef->fRefTime = 0; - - vRef->SetDirty(true); - vRef->SetRebuiltSinceUsed(true); - vRef->fData = nullptr; - - vRef->SetVolatile(vRef->Volatile() || owner->AreVertsVolatile()); - - vRef->fIndex = idx; - - owner->SetVertexBufferRef(idx, vRef); - hsRefCnt_SafeUnRef(vRef); -} - -// ICheckStaticVertexBuffer /////////////////////////////////////////////////////////////////////// -// Ensure a static vertex buffer has any D3D resources necessary for rendering created and filled -// with proper vertex data. -void plDXPipeline::ICheckStaticVertexBuffer(plDXVertexBufferRef* vRef, plGBufferGroup* owner, uint32_t idx) -{ - hsAssert(!vRef->Volatile(), "Creating a managed vertex buffer for a volatile buffer ref"); - - if( !vRef->fD3DBuffer ) - { - // Okay, haven't done this one. - - DWORD fvfFormat = IGetBufferD3DFormat(vRef->fFormat); - - - D3DPOOL poolType = D3DPOOL_MANAGED; -// DWORD usage = D3DUSAGE_WRITEONLY; - DWORD usage = 0; - const int numVerts = vRef->fCount; - const int vertSize = vRef->fVertexSize; - fManagedAlloced = true; - if( FAILED( fD3DDevice->CreateVertexBuffer( numVerts * vertSize, - usage, - fvfFormat, - poolType, - &vRef->fD3DBuffer, nullptr))) - { - hsAssert( false, "CreateVertexBuffer() call failed!" ); - vRef->fD3DBuffer = nullptr; - return; - } - PROFILE_POOL_MEM(poolType, numVerts * vertSize, true, ST_LITERAL("VtxBuff")); - - // Record that we've allocated this into managed memory, in case we're - // fighting that NVidia driver bug. Search for OSVERSION for mor info. - AllocManagedVertex(numVerts * vertSize); - - // Fill in the vertex data. - IFillStaticVertexBufferRef(vRef, owner, idx); - - // This is currently a no op, but this would let the buffer know it can - // unload the system memory copy, since we have a managed version now. - owner->PurgeVertBuffer(idx); - } -} - -// IFillStaticVertexBufferRef ////////////////////////////////////////////////// -// BufferRef is set up, just copy the data in. -// This is uglied up hugely by the insane non-interleaved data case with cells -// and whatever else. -void plDXPipeline::IFillStaticVertexBufferRef(plDXVertexBufferRef *ref, plGBufferGroup *group, uint32_t idx) -{ - IDirect3DVertexBuffer9* vertexBuff = ref->fD3DBuffer; - - if( !vertexBuff ) - { - // We most likely already warned about this earlier, best to just quietly return now - return; - } - - const uint32_t vertSize = ref->fVertexSize; - const uint32_t vertStart = group->GetVertBufferStart(idx) * vertSize; - const uint32_t size = group->GetVertBufferEnd(idx) * vertSize - vertStart; - if( !size ) - return; - - /// Lock the buffer - uint8_t* ptr; - if( FAILED( vertexBuff->Lock( vertStart, size, (void **)&ptr, group->AreVertsVolatile() ? D3DLOCK_DISCARD : 0 ) ) ) - { - hsAssert( false, "Failed to lock vertex buffer for writing" ); - } - - if( ref->fData ) - { - memcpy(ptr, ref->fData + vertStart, size); - } - else - { - hsAssert(0 == vertStart, "Offsets on non-interleaved data not supported"); - hsAssert(group->GetVertBufferCount(idx) * vertSize == size, "Trailing dead space on non-interleaved data not supported"); - - const uint32_t vertSmallSize = group->GetVertexLiteStride() - sizeof( hsPoint3 ) * 2; - uint8_t* srcVPtr = group->GetVertBufferData(idx); - plGBufferColor* const srcCPtr = group->GetColorBufferData( idx ); - - const int numCells = group->GetNumCells(idx); - int i; - for( i = 0; i < numCells; i++ ) - { - plGBufferCell *cell = group->GetCell( idx, i ); - - if( cell->fColorStart == (uint32_t)-1 ) - { - /// Interleaved, do straight copy - memcpy( ptr, srcVPtr + cell->fVtxStart, cell->fLength * vertSize ); - ptr += cell->fLength * vertSize; - } - else - { - /// Separated, gotta interleave - uint8_t* tempVPtr = srcVPtr + cell->fVtxStart; - plGBufferColor* tempCPtr = srcCPtr + cell->fColorStart; - int j; - for( j = 0; j < cell->fLength; j++ ) - { - memcpy( ptr, tempVPtr, sizeof( hsPoint3 ) * 2 ); - ptr += sizeof( hsPoint3 ) * 2; - tempVPtr += sizeof( hsPoint3 ) * 2; - - memcpy( ptr, &tempCPtr->fDiffuse, sizeof( uint32_t ) ); - ptr += sizeof( uint32_t ); - memcpy( ptr, &tempCPtr->fSpecular, sizeof( uint32_t ) ); - ptr += sizeof( uint32_t ); - - memcpy( ptr, tempVPtr, vertSmallSize ); - ptr += vertSmallSize; - tempVPtr += vertSmallSize; - tempCPtr++; - } - } - } - } - - /// Unlock and clean up - vertexBuff->Unlock(); - ref->SetRebuiltSinceUsed(true); - ref->SetDirty(false); -} - -void plDXPipeline::IFillVolatileVertexBufferRef(plDXVertexBufferRef* ref, plGBufferGroup* group, uint32_t idx) -{ - uint8_t* dst = ref->fData; - uint8_t* src = group->GetVertBufferData(idx); - - size_t uvChanSize = plGBufferGroup::CalcNumUVs(group->GetVertexFormat()) * sizeof(float) * 3; - uint8_t numWeights = (group->GetVertexFormat() & plGBufferGroup::kSkinWeightMask) >> 4; - - for (uint32_t i = 0; i < ref->fCount; ++i) { - inlCopy(src, dst); // pre-pos - src += numWeights * sizeof(float); // weights - if (group->GetVertexFormat() & plGBufferGroup::kSkinIndices) - inlSkip(src); // indices - inlCopy(src, dst); // pre-normal - inlCopy(src, dst); // diffuse - inlCopy(src, dst); // specular - - // UVWs - memcpy(dst, src, uvChanSize); - src += uvChanSize; - dst += uvChanSize; - } -} // OpenAccess //////////////////////////////////////////////////////////////////////////////////////// // Lock the managed buffer and setup the accessSpan to point into the buffers data. @@ -8371,150 +7905,6 @@ bool plDXPipeline::CloseAccess(plAccessSpan& dst) return true; } -// CheckVertexBufferRef ///////////////////////////////////////////////////// -// Make sure the buffer group has a valid buffer ref and that it is up to date. -void plDXPipeline::CheckVertexBufferRef(plGBufferGroup* owner, uint32_t idx) -{ - // First, do we have a device ref at this index? - plDXVertexBufferRef* vRef = (plDXVertexBufferRef*)owner->GetVertexBufferRef(idx); - // If not - if( !vRef ) - { - // Make the blank ref - vRef = new plDXVertexBufferRef; - - ISetupVertexBufferRef(owner, idx, vRef); - - } - if( !vRef->IsLinked() ) - vRef->Link( &fVtxBuffRefList ); - - // One way or another, we now have a vbufferref[idx] in owner. - // Now, does it need to be (re)filled? - // If the owner is volatile, then we hold off. It might not - // be visible, and we might need to refill it again if we - // have an overrun of our dynamic D3D buffer. - if( !vRef->Volatile() ) - { - if( fAllocUnManaged ) - return; - - // If it's a static buffer, allocate a D3D vertex buffer for it. Otherwise, it'll - // be sharing the global D3D dynamic buffer, and marked as volatile. - ICheckStaticVertexBuffer(vRef, owner, idx); - - // Might want to remove this assert, and replace it with a dirty check if - // we have static buffers that change very seldom rather than never. - hsAssert(!vRef->IsDirty(), "Non-volatile vertex buffers should never get dirty"); - } - else - { - // Make sure we're going to be ready to fill it. - - if( !vRef->fData && (vRef->fFormat != owner->GetVertexFormat()) ) - { - vRef->fData = new uint8_t[vRef->fCount * vRef->fVertexSize]; - IFillVolatileVertexBufferRef(vRef, owner, idx); - } - } -} - -// CheckIndexBufferRef ///////////////////////////////////////////////////// -// Make sure the buffer group has an index buffer ref and that its data is current. -void plDXPipeline::CheckIndexBufferRef(plGBufferGroup* owner, uint32_t idx) -{ - plDXIndexBufferRef* iRef = (plDXIndexBufferRef*)owner->GetIndexBufferRef(idx); - if( !iRef ) - { - // Create one from scratch. - - iRef = new plDXIndexBufferRef; - - ISetupIndexBufferRef(owner, idx, iRef); - - } - if( !iRef->IsLinked() ) - iRef->Link(&fIdxBuffRefList); - - // Make sure it has all D3D resources created. - ICheckIndexBuffer(iRef); - - // If it's dirty, refill it. - if( iRef->IsDirty() ) - IFillIndexBufferRef(iRef, owner, idx); -} - -// IFillIndexBufferRef //////////////////////////////////////////////////////////// -// Refresh the D3D index buffer from the plasma index buffer. -void plDXPipeline::IFillIndexBufferRef(plDXIndexBufferRef* iRef, plGBufferGroup* owner, uint32_t idx) -{ - uint32_t startIdx = owner->GetIndexBufferStart(idx); - uint32_t size = (owner->GetIndexBufferEnd(idx) - startIdx) * sizeof(uint16_t); - if( !size ) - return; - - DWORD lockFlags = iRef->Volatile() ? D3DLOCK_DISCARD : 0; - uint16_t* destPtr = nullptr; - if( FAILED( iRef->fD3DBuffer->Lock(startIdx * sizeof(uint16_t), size, (void **)&destPtr, lockFlags) ) ) - { - hsAssert( false, "Cannot lock index buffer for writing" ); - return; - } - - memcpy( destPtr, owner->GetIndexBufferData(idx) + startIdx, size ); - - iRef->fD3DBuffer->Unlock(); - - iRef->SetDirty( false ); - -} - -// ICheckIndexBuffer //////////////////////////////////////////////////////// -// Make sure index buffer ref has any D3D resources it needs. -void plDXPipeline::ICheckIndexBuffer(plDXIndexBufferRef* iRef) -{ - if( !iRef->fD3DBuffer && iRef->fCount ) - { - D3DPOOL poolType = fAllocUnManaged ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; - DWORD usage = D3DUSAGE_WRITEONLY; - iRef->SetVolatile(false); - if( FAILED( fD3DDevice->CreateIndexBuffer( sizeof( uint16_t ) * iRef->fCount, - usage, - D3DFMT_INDEX16, - poolType, - &iRef->fD3DBuffer, nullptr))) - { - hsAssert( false, "CreateIndexBuffer() call failed!" ); - iRef->fD3DBuffer = nullptr; - return; - } - PROFILE_POOL_MEM(poolType, sizeof(uint16_t) * iRef->fCount, true, ST_LITERAL("IndexBuff")); - - iRef->fPoolType = poolType; - iRef->SetDirty(true); - iRef->SetRebuiltSinceUsed(true); - } -} - -// ISetupIndexBufferRef //////////////////////////////////////////////////////////////// -// Initialize the index buffer ref, but don't create anything for it. -void plDXPipeline::ISetupIndexBufferRef(plGBufferGroup* owner, uint32_t idx, plDXIndexBufferRef* iRef) -{ - uint32_t numIndices = owner->GetIndexBufferCount(idx); - iRef->fCount = numIndices; - iRef->fOwner = owner; - iRef->fIndex = idx; - iRef->fRefTime = 0; - - iRef->SetDirty(true); - iRef->SetRebuiltSinceUsed(true); - - owner->SetIndexBufferRef(idx, iRef); - hsRefCnt_SafeUnRef(iRef); - - iRef->SetVolatile(owner->AreIdxVolatile()); -} - //// ISoftwareVertexBlend /////////////////////////////////////////////////////// // Emulate matrix palette operations in software. The big difference between the hardware // and software versions is we only want to lock the vertex buffer once and blend all the @@ -8622,29 +8012,6 @@ bool plDXPipeline::ISoftwareVertexBlend(plDrawableSpans* drawable, const st return true; } -// IBeginAllocUnmanaged /////////////////////////////////////////////////////////////////// -// Before allocating anything into POOL_DEFAULT, we must evict managed memory. -// See LoadResources. -void plDXPipeline::IBeginAllocUnManaged() -{ - // Flush out all managed resources to make room for unmanaged resources. - fD3DDevice->EvictManagedResources(); - - fManagedAlloced = false; - fAllocUnManaged = true; // we're currently only allocating POOL_DEFAULT -} - -// IEndAllocUnManged. -// Before allocating anything into POOL_DEFAULT, we must evict managed memory. -// See LoadResources. -void plDXPipeline::IEndAllocUnManaged() -{ - fAllocUnManaged = false; - - // Flush the (should be empty) resource manager to reset its internal allocation pool. - fD3DDevice->EvictManagedResources(); -} - // LoadResources /////////////////////////////////////////////////////////////////////// // Basically, we have to load anything that goes into POOL_DEFAULT before // anything into POOL_MANAGED, or the memory manager gets confused. @@ -8664,7 +8031,7 @@ void plDXPipeline::LoadResources() IInitDeviceState(); // 9700 THRASH // Evict mananged memory. - IBeginAllocUnManaged(); + fDevice.BeginAllocUnManaged(); // Release everything we have in POOL_DEFAULT. IReleaseDynamicBuffers(); @@ -8688,7 +8055,7 @@ void plDXPipeline::LoadResources() IFillAvRTPool(); // We should have everything POOL_DEFAULT we need now. - IEndAllocUnManaged(); + fDevice.EndAllocUnManaged(); // Force a create of all our static D3D vertex buffers. #define MF_PRELOAD_MANAGEDBUFFERS @@ -9237,7 +8604,7 @@ void plDXPipeline::IRenderAuxSpan(const plSpan& span, const plAuxSpan* aux) r = fD3DDevice->SetIndices( iRef->fD3DBuffer ); hsAssert( r == D3D_OK, "Error trying to set the indices!" ); - plRenderTriListFunc render(fD3DDevice, iRef->fOffset, aux->fVStartIdx, aux->fVLength, aux->fIStartIdx, aux->fILength/3); + plDXRenderTriListFunc render(fD3DDevice, iRef->fOffset, aux->fVStartIdx, aux->fVLength, aux->fIStartIdx, aux->fILength/3); // Now just loop through the aux material, rendering in as many passes as it takes. hsGMaterial* material = aux->fMaterial; @@ -9359,7 +8726,7 @@ void plDXPipeline::IRenderBufferSpan( const plIcicle& span, iRef->SetRebuiltSinceUsed(false); } - plRenderTriListFunc render(fD3DDevice, iRef->fOffset, vStart, vLength, iStart, iLength/3); + plDXRenderTriListFunc render(fD3DDevice, iRef->fOffset, vStart, vLength, iStart, iLength/3); plProfile_EndTiming(RenderBuff); ILoopOverLayers(render, material, span); @@ -9661,28 +9028,6 @@ long plDXPipeline::IGetBufferD3DFormat( uint8_t format ) const return fmt; } -//// IGetBufferFormatSize ///////////////////////////////////////////////////// -// Calculate the vertex stride from the given format. -uint32_t plDXPipeline::IGetBufferFormatSize( uint8_t format ) const -{ - uint32_t size = sizeof( float ) * 6 + sizeof( uint32_t ) * 2; // Position and normal, and two packed colors - - - switch( format & plGBufferGroup::kSkinWeightMask ) - { - case plGBufferGroup::kSkinNoWeights: - break; - case plGBufferGroup::kSkin1Weight: - size += sizeof(float); - break; - default: - hsAssert( false, "Invalid skin weight value in IGetBufferFormatSize()" ); - } - - size += sizeof( float ) * 3 * plGBufferGroup::CalcNumUVs( format ); - - return size; -} /////////////////////////////////////////////////////////////////////////////// //// Plate and PlateManager Functions ///////////////////////////////////////// @@ -10814,7 +10159,7 @@ void plDXPipeline::IRenderShadowCasterSpan(plShadowSlave* slave, plDrawableSpans uint32_t iStart = span.fIPackedIdx; uint32_t iLength= span.fILength; - plRenderTriListFunc render(fD3DDevice, iRef->fOffset, vStart, vLength, iStart, iLength/3); + plDXRenderTriListFunc render(fD3DDevice, iRef->fOffset, vStart, vLength, iStart, iLength/3); static hsMatrix44 emptyMatrix; hsMatrix44 m = emptyMatrix; @@ -11189,7 +10534,7 @@ bool plDXPipeline::IPopShadowCastState(plShadowSlave* slave) // must be created before we start creating things in POOL_MANAGED. void plDXPipeline::IMakeRenderTargetPools() { - hsAssert(!fManagedAlloced, "Allocating rendertargets with managed resources alloced"); + hsAssert(!fDevice.fManagedAlloced, "Allocating rendertargets with managed resources alloced"); IReleaseRenderTargetPools(); // Just to be sure. // Numbers of render targets to be created for each size. @@ -11450,18 +10795,6 @@ void plDXPipeline::IPreprocessShadows() plProfile_EndTiming(PrepShadows); } -// IClearShadowSlaves /////////////////////////////////////////////////////////////////////////// -// At EndRender(), we need to clear our list of shadow slaves. They are only valid for one frame. -void plDXPipeline::IClearShadowSlaves() -{ - for (plShadowSlave* shadow : fShadows) - { - const plShadowCaster* caster = shadow->fCaster; - caster->GetKey()->UnRefObject(); - } - fShadows.clear(); -} - // IRenderShadowsOntoSpan ///////////////////////////////////////////////////////////////////// // After doing the usual render for a span (all passes), we call the following. diff --git a/Sources/Plasma/FeatureLib/pfDXPipeline/plDXPipeline.h b/Sources/Plasma/FeatureLib/pfDXPipeline/plDXPipeline.h index 3b33cdcb44..31a45649ab 100644 --- a/Sources/Plasma/FeatureLib/pfDXPipeline/plDXPipeline.h +++ b/Sources/Plasma/FeatureLib/pfDXPipeline/plDXPipeline.h @@ -118,15 +118,6 @@ typedef LPDIRECT3D9 (WINAPI * Direct3DCreateProc)( UINT sdkVersion ); //// Helper Classes /////////////////////////////////////////////////////////// -//// The RenderPrimFunc lets you have one function which does a lot of stuff -// around the actual call to render whatever type of primitives you have, instead -// of duplicating everything because the one line to render is different. -class plRenderPrimFunc -{ -public: - virtual bool RenderPrims() const = 0; // return true on error -}; - //// DX-specific Plate Manager implementation class plDXPlateManager : public plPlateManager { @@ -177,6 +168,8 @@ class plStatusLogDrawer; class plDXPipeline : public pl3DPipeline { + friend class plDXDevice; + protected: enum { kCapsNone = 0x00000000, @@ -217,9 +210,6 @@ class plDXPipeline : public pl3DPipeline uint32_t fNextDynVtx; uint32_t fDynVtxSize; IDirect3DVertexBuffer9* fDynVtxBuff; - bool fManagedAlloced; - bool fAllocUnManaged; - // States plDXGeneralSettings fSettings; @@ -228,7 +218,6 @@ class plDXPipeline : public pl3DPipeline bool fDevWasLost; plDXTextureRef* fTextureRefList; - plTextFont* fTextFontRefList; plDXRenderTargetRef* fRenderTargetRefList; plDXVertexShader* fVShaderRefList; plDXPixelShader* fPShaderRefList; @@ -285,19 +274,9 @@ class plDXPipeline : public pl3DPipeline bool fForceDeviceReset; - void IBeginAllocUnManaged(); - void IEndAllocUnManaged(); - bool IRefreshDynVertices(plGBufferGroup* group, plDXVertexBufferRef* vRef); bool ICheckAuxBuffers(const plAuxSpan* span); bool ICheckDynBuffers(plDrawableSpans* drawable, plGBufferGroup* group, const plSpan* span); - void ICheckStaticVertexBuffer(plDXVertexBufferRef* vRef, plGBufferGroup* owner, uint32_t idx); - void ICheckIndexBuffer(plDXIndexBufferRef* iRef); - void IFillStaticVertexBufferRef(plDXVertexBufferRef *ref, plGBufferGroup *group, uint32_t idx); - void IFillVolatileVertexBufferRef(plDXVertexBufferRef* ref, plGBufferGroup* group, uint32_t idx); - void IFillIndexBufferRef(plDXIndexBufferRef* iRef, plGBufferGroup* owner, uint32_t idx); - void ISetupVertexBufferRef(plGBufferGroup* owner, uint32_t idx, plDXVertexBufferRef* vRef); - void ISetupIndexBufferRef(plGBufferGroup* owner, uint32_t idx, plDXIndexBufferRef* iRef); void ICreateDynamicBuffers(); void IReleaseDynamicBuffers(); @@ -307,7 +286,6 @@ class plDXPipeline : public pl3DPipeline // Rendering bool IFlipSurface(); long IGetBufferD3DFormat(uint8_t format) const; - uint32_t IGetBufferFormatSize(uint8_t format) const; void IGetVisibleSpans(plDrawableSpans* drawable, std::vector& visList, plVisMgr* visMgr); bool ILoopOverLayers(const plRenderPrimFunc& render, hsGMaterial* material, const plSpan& span); void IRenderBufferSpan( const plIcicle& span, @@ -362,11 +340,6 @@ class plDXPipeline : public pl3DPipeline void IBottomLayer(); // Push special effects - plLayerInterface* IPushOverBaseLayer(plLayerInterface* li); - plLayerInterface* IPopOverBaseLayer(plLayerInterface* li); - plLayerInterface* IPushOverAllLayer(plLayerInterface* li); - plLayerInterface* IPopOverAllLayer(plLayerInterface* li); - int ISetNumActivePiggyBacks(); void IPushPiggyBacks(hsGMaterial* mat); void IPopPiggyBacks(); @@ -400,7 +373,6 @@ class plDXPipeline : public pl3DPipeline // Visualization of active occluders void IMakeOcclusionSnap(); - bool IAvatarSort(plDrawableSpans* d, const std::vector& visList); void IBlendVertsIntoBuffer( plSpan* span, hsMatrix44* matrixPalette, int numMatrices, const uint8_t *src, uint8_t format, uint32_t srcStride, @@ -459,7 +431,6 @@ class plDXPipeline : public pl3DPipeline // Transforms D3DMATRIX& IMatrix44ToD3DMatrix( D3DMATRIX& dst, const hsMatrix44& src ); void ISetCullMode(bool flip=false); - bool inline IIsViewLeftHanded(); bool IGetClearViewPort(D3DRECT& r); void ISetupTransforms(plDrawableSpans* drawable, const plSpan& span, hsMatrix44& lastL2W); @@ -480,7 +451,6 @@ class plDXPipeline : public pl3DPipeline /////// Shadow internals // Generation - void IClearShadowSlaves(); void IPreprocessShadows(); bool IPrepShadowCaster(const plShadowCaster* caster); void IRenderShadowCasterSpan(plShadowSlave* slave, plDrawableSpans* drawable, const plIcicle& span); @@ -559,10 +529,6 @@ class plDXPipeline : public pl3DPipeline // Create a debug text font object plTextFont* MakeTextFont(ST::string face, uint16_t size) override; - // Create and/or Refresh geometry buffers - void CheckVertexBufferRef(plGBufferGroup* owner, uint32_t idx) override; - void CheckIndexBufferRef(plGBufferGroup* owner, uint32_t idx) override; - bool OpenAccess(plAccessSpan& dst, plDrawableSpans* d, const plVertexSpan* span, bool readOnly) override; bool CloseAccess(plAccessSpan& acc) override; @@ -597,7 +563,7 @@ class plDXPipeline : public pl3DPipeline /// Error handling ST::string GetErrorString() override; - bool ManagedAlloced() const { return fManagedAlloced; } + bool ManagedAlloced() const { return fDevice.fManagedAlloced; } virtual void GetSupportedColorDepths(std::vector &ColorDepths); void GetSupportedDisplayModes(std::vector *res, int ColorDepth = 32) override; diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/CMakeLists.txt b/Sources/Plasma/FeatureLib/pfGLPipeline/CMakeLists.txt index b5e3dd5575..963bb4b2d0 100644 --- a/Sources/Plasma/FeatureLib/pfGLPipeline/CMakeLists.txt +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/CMakeLists.txt @@ -2,19 +2,30 @@ set(pfGLPipeline_SOURCES plGLDevice.cpp plGLEnumerate.cpp plGLDeviceRefs.cpp + plGLMaterialShaderRef.cpp plGLPipeline.cpp plGLPlateManager.cpp + plGLTextFont.cpp + $<$:plCGLDevice.cpp> + plEGLDevice.cpp + $<$:plWGLDevice.cpp> ) set(pfGLPipeline_HEADERS plGLDevice.h plGLDeviceRef.h + plGLMaterialShaderRef.h plGLPipeline.h pfGLPipelineCreatable.h plGLPlateManager.h + plGLTextFont.h + plCGLDevice.h + plEGLDevice.h + plWGLDevice.h ) plasma_library(pfGLPipeline SOURCES ${pfGLPipeline_SOURCES} ${pfGLPipeline_HEADERS}) +plasma_target_simd_sources(pfGLPipeline SSE3 SSE41 plGLPipeline.cpp) target_link_libraries(pfGLPipeline PUBLIC CoreLib diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plCGLDevice.cpp b/Sources/Plasma/FeatureLib/pfGLPipeline/plCGLDevice.cpp new file mode 100644 index 0000000000..3e27bbde7a --- /dev/null +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plCGLDevice.cpp @@ -0,0 +1,187 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "plCGLDevice.h" + +#ifdef HS_BUILD_FOR_MACOS +#include + +bool plCGLDevice::Enumerate(hsG3DDeviceRecord& record) +{ + bool result = false; + + IGNORE_WARNINGS_BEGIN("deprecated-declarations") + CGLPixelFormatObj pix = nullptr; + CGLContextObj ctx = nullptr; + + do { + CGLPixelFormatAttribute attribs[6] = { + kCGLPFAAccelerated, + kCGLPFANoRecovery, + kCGLPFADoubleBuffer, +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 + // OpenGL profiles introduced in 10.7 + kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core, +#endif + (CGLPixelFormatAttribute) 0 + }; + + int nPix = 0; + if (CGLChoosePixelFormat(attribs, &pix, &nPix) != kCGLNoError || nPix == 0) + break; + + if (CGLCreateContext(pix, nullptr, &ctx) != kCGLNoError) + break; + + if (CGLSetCurrentContext(ctx) != kCGLNoError) + break; + + if (epoxy_gl_version() < 33) + break; + + record.SetG3DDeviceType(hsG3DDeviceSelector::kDevTypeOpenGL); + record.SetDriverName("OpenGL.framework"); + record.SetDeviceDesc(reinterpret_cast(glGetString(GL_RENDERER))); + record.SetDriverDesc(reinterpret_cast(glGetString(GL_VENDOR))); + record.SetDriverVersion(reinterpret_cast(glGetString(GL_VERSION))); + + result = true; + } while (0); + + // Cleanup: + if (ctx) { + CGLSetCurrentContext(nullptr); + CGLReleaseContext(ctx); + } + + if (pix) + CGLReleasePixelFormat(pix); + + IGNORE_WARNINGS_END + + return result; +} + + +plCGLDevice* plCGLDevice::TryInit(hsWindowHndl window, hsWindowHndl device, ST::string& error) +{ + IGNORE_WARNINGS_BEGIN("deprecated-declarations") + + CGLPixelFormatObj pix = nullptr; + CGLContextObj ctx = nullptr; + + do { + CGLPixelFormatAttribute attribs[6] = { + kCGLPFAAccelerated, + kCGLPFANoRecovery, + kCGLPFADoubleBuffer, +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 + // OpenGL profiles introduced in 10.7 + kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core, +#endif + (CGLPixelFormatAttribute) 0 + }; + + int nPix = 0; + if (CGLChoosePixelFormat(attribs, &pix, &nPix) != kCGLNoError || nPix == 0) { + error = ST_LITERAL("Could not choose appropriate config"); + break; + } + + if (CGLCreateContext(pix, nullptr, &ctx) != kCGLNoError) { + error = ST_LITERAL("Unable to create rendering context"); + break; + } + + if (CGLSetCurrentContext(ctx) != kCGLNoError) { + error = ST_LITERAL("Failed to attach CGL context to surface"); + break; + } + + CGLReleasePixelFormat(pix); + + // Successfully initialized + return new plCGLDevice(window, device, ctx); + } while (0); + + // Cleanup for failure case: + if (ctx) { + CGLSetCurrentContext(nullptr); + CGLReleaseContext(ctx); + } + + if (pix) + CGLReleasePixelFormat(pix); + + IGNORE_WARNINGS_END + + return nullptr; +} + + +plCGLDevice::plCGLDevice(hsWindowHndl window, hsWindowHndl device, CGLContextObj context) + : plGLDeviceImpl(window, device), fContext(context) +{ } + +void plCGLDevice::Shutdown() +{ + IGNORE_WARNINGS_BEGIN("deprecated-declarations") + + CGLSetCurrentContext(nullptr); + CGLReleaseContext(fContext); + fContext = nullptr; + + IGNORE_WARNINGS_END +} + +bool plCGLDevice::BeginRender(ST::string& error) +{ + return true; +} + +bool plCGLDevice::EndRender(ST::string& error) +{ + return true; +} + +#endif // HS_BUILD_FOR_MACOS + diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plCGLDevice.h b/Sources/Plasma/FeatureLib/pfGLPipeline/plCGLDevice.h new file mode 100644 index 0000000000..da72833ff3 --- /dev/null +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plCGLDevice.h @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _plCGLDevice_h_ +#define _plCGLDevice_h_ + +#include "HeadSpin.h" + +#ifdef HS_BUILD_FOR_MACOS +#include "plGLDevice.h" +#include "plPipeline/hsG3DDeviceSelector.h" +#include + +class plCGLDevice : public plGLDeviceImpl +{ +protected: + CGLContextObj fContext; + + plCGLDevice(hsWindowHndl window, hsWindowHndl device, CGLContextObj context); + +public: + static bool Enumerate(hsG3DDeviceRecord& record); + static plCGLDevice* TryInit(hsWindowHndl window, hsWindowHndl device, ST::string& error); + + void Shutdown() override; + bool BeginRender(ST::string& error) override; + bool EndRender(ST::string& error) override; +}; + +#endif // HS_BUILD_FOR_MACOS + +#endif // _plCGLDevice_h_ + + diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plEGLDevice.cpp b/Sources/Plasma/FeatureLib/pfGLPipeline/plEGLDevice.cpp new file mode 100644 index 0000000000..9811b8e16f --- /dev/null +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plEGLDevice.cpp @@ -0,0 +1,250 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "plEGLDevice.h" + +#ifdef USE_EGL + +bool plEGLDevice::Enumerate(hsG3DDeviceRecord& record) +{ + bool result = false; + EGLDisplay display = EGL_NO_DISPLAY; + EGLContext context = EGL_NO_CONTEXT; + EGLSurface surface = EGL_NO_SURFACE; + + do { + display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (display == EGL_NO_DISPLAY) + break; + + if (!eglInitialize(display, nullptr, nullptr)) + break; + + if (!eglBindAPI(EGL_OPENGL_API)) + break; + + /* Set up the config attributes for EGL */ + EGLConfig config; + EGLint config_count; + EGLint config_attrs[] = { + EGL_BUFFER_SIZE, 24, + EGL_DEPTH_SIZE, 24, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_NONE + }; + + if (!eglChooseConfig(display, config_attrs, &config, 1, &config_count) || config_count != 1) + break; + + EGLint ctx_attrs[] = { + EGL_CONTEXT_MAJOR_VERSION, 3, + EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + EGL_NONE + }; + + context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctx_attrs); + if (context == EGL_NO_CONTEXT) + break; + + EGLint pbuf_attrs[] = { + EGL_WIDTH, 800, + EGL_HEIGHT, 600, + EGL_NONE + }; + + surface = eglCreatePbufferSurface(display, config, pbuf_attrs); + if (surface == EGL_NO_SURFACE) + break; + + if (!eglMakeCurrent(display, surface, surface, context)) + break; + + if (epoxy_gl_version() < 33) + break; + + record.SetG3DDeviceType(hsG3DDeviceSelector::kDevTypeOpenGL); + record.SetDriverName("EGL"); + record.SetDeviceDesc(reinterpret_cast(glGetString(GL_RENDERER))); + record.SetDriverDesc(reinterpret_cast(glGetString(GL_VENDOR))); + record.SetDriverVersion(reinterpret_cast(glGetString(GL_VERSION))); + + result = true; + } while (0); + + // Cleanup: + if (surface != EGL_NO_SURFACE) { + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(display, surface); + } + + if (context != EGL_NO_CONTEXT) + eglDestroyContext(display, context); + + if (display != EGL_NO_DISPLAY) + eglTerminate(display); + + return result; +} + + +plEGLDevice* plEGLDevice::TryInit(hsWindowHndl window, hsWindowHndl device, ST::string& error) +{ + EGLDisplay display = EGL_NO_DISPLAY; + EGLContext context = EGL_NO_CONTEXT; + EGLSurface surface = EGL_NO_SURFACE; + + do { + if (!eglBindAPI(EGL_OPENGL_API)) { + error = ST_LITERAL("Could not bind to OpenGL API"); + break; + } + + /* Set up the display */ + display = eglGetDisplay(device); + if (display == EGL_NO_DISPLAY) { + error = ST_LITERAL("Could not get the display"); + break; + } + + if (!eglInitialize(display, nullptr, nullptr)) { + error = ST_LITERAL("Could not initialize the display"); + break; + } + + /* Set up the config attributes for EGL */ + EGLConfig config; + EGLint config_count; + EGLint config_attrs[] = { + EGL_BUFFER_SIZE, 24, + EGL_DEPTH_SIZE, 24, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_NONE + }; + + if (!eglChooseConfig(display, config_attrs, &config, 1, &config_count) || config_count != 1) { + error = ST_LITERAL("Could not choose appropriate config"); + break; + } + + /* Set up the GL context */ + EGLint ctx_attrs[] = { + EGL_CONTEXT_MAJOR_VERSION, 3, + EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + EGL_NONE + }; + + context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctx_attrs); + if (context == EGL_NO_CONTEXT) { + error = ST_LITERAL("Unable to create rendering context"); + break; + } + + /* Set up the rendering surface */ + surface = eglCreateWindowSurface(display, config, (EGLNativeWindowType)window, nullptr); + if (surface == EGL_NO_SURFACE) { + error = ST_LITERAL("Unable to create rendering surface"); + break; + } + + /* Associate everything */ + if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) { + error = ST_LITERAL("Failed to attach EGL context to surface"); + break; + } + + // Successfully initialized + return new plEGLDevice(window, device, display, context, surface); + } while (0); + + // Cleanup for failure case: + if (surface != EGL_NO_SURFACE) { + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(display, surface); + } + + if (context != EGL_NO_CONTEXT) + eglDestroyContext(display, context); + + if (display != EGL_NO_DISPLAY) + eglTerminate(display); + + return nullptr; +} + + +plEGLDevice::plEGLDevice(hsWindowHndl window, hsWindowHndl device, EGLDisplay display, EGLContext context, EGLSurface surface) + : plGLDeviceImpl(window, device), fDisplay(display), fContext(context), fSurface(surface) +{ } + +void plEGLDevice::Shutdown() +{ + eglDestroySurface(fDisplay, fSurface); + fSurface = EGL_NO_SURFACE; + + eglDestroyContext(fDisplay, fContext); + fContext = EGL_NO_CONTEXT; + + eglTerminate(fDisplay); + fDisplay = EGL_NO_DISPLAY; +} + +bool plEGLDevice::BeginRender(ST::string& error) +{ + if (eglMakeCurrent(fDisplay, fSurface, fSurface, fContext) == EGL_FALSE) { + error = ST_LITERAL("Failed to attach EGL context to surface"); + return false; + } + + return true; +} + +bool plEGLDevice::EndRender(ST::string& error) +{ + if (eglSwapBuffers(fDisplay, fSurface) == EGL_FALSE) { + error = ST_LITERAL("Failed to swap buffers"); + return false; + } + + return true; +} +#endif // USE_EGL diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plEGLDevice.h b/Sources/Plasma/FeatureLib/pfGLPipeline/plEGLDevice.h new file mode 100644 index 0000000000..4879f6db07 --- /dev/null +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plEGLDevice.h @@ -0,0 +1,73 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _plEGLDevice_h_ +#define _plEGLDevice_h_ + +#include "HeadSpin.h" +#include "plGLDevice.h" + +#ifdef USE_EGL +#include + +#include "plPipeline/hsG3DDeviceSelector.h" + +class plEGLDevice : public plGLDeviceImpl +{ +protected: + EGLDisplay fDisplay; + EGLContext fContext; + EGLSurface fSurface; + + plEGLDevice(hsWindowHndl window, hsWindowHndl device, EGLDisplay display, EGLContext context, EGLSurface surface); + +public: + static bool Enumerate(hsG3DDeviceRecord& record); + static plEGLDevice* TryInit(hsWindowHndl window, hsWindowHndl device, ST::string& error); + + void Shutdown() override; + bool BeginRender(ST::string& error) override; + bool EndRender(ST::string& error) override; +}; + +#endif // USE_EGL + +#endif // _plEGLDevice_h_ diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLDevice.cpp b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLDevice.cpp index 1c27752f61..c9475eef14 100644 --- a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLDevice.cpp +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLDevice.cpp @@ -40,29 +40,612 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com *==LICENSE==*/ +#include + +#include "hsThread.h" + #include "plGLDevice.h" +#include "plGLPipeline.h" + +#include "plCGLDevice.h" +#include "plEGLDevice.h" +#include "plWGLDevice.h" + +#include "plDrawable/plGBufferGroup.h" +#include "plGImage/plMipmap.h" +#include "plGImage/plCubicEnvironmap.h" +#include "plPipeline/plRenderTarget.h" +#include "plStatusLog/plStatusLog.h" + +#ifdef HS_DEBUGGING +static void GLAPIENTRY plGLDebugLog(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) +{ + if (severity <= GL_DEBUG_SEVERITY_MEDIUM) { // Yes, higher is a lower enum value + plStatusLog::AddLineSF("pipeline.log", "[GL] {}{}", (type == GL_DEBUG_TYPE_ERROR ? "** ERROR **: " : ""), message); + } +} +#endif + +static float kIdentityMatrix[16] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f +}; + +GLfloat* hsMatrix2GL(const hsMatrix44& src, GLfloat* dst) +{ + if (src.fFlags & hsMatrix44::kIsIdent) + return static_cast(memcpy(dst, kIdentityMatrix, sizeof(GLfloat) * 16)); + else + return static_cast(memcpy(dst, src.fMap, sizeof(GLfloat) * 16)); +} plGLDevice::plGLDevice() - : fErrorMsg() + : fErrorMsg(), fPipeline(), fImpl(), fWindow(), fDevice(), fActiveThread(), fCurrentProgram() { + memcpy(fMatrixL2W, kIdentityMatrix, sizeof(GLfloat) * 16); + memcpy(fMatrixW2L, kIdentityMatrix, sizeof(GLfloat) * 16); + memcpy(fMatrixW2C, kIdentityMatrix, sizeof(GLfloat) * 16); + memcpy(fMatrixC2W, kIdentityMatrix, sizeof(GLfloat) * 16); + memcpy(fMatrixProj, kIdentityMatrix, sizeof(GLfloat) * 16); +} + +void plGLDevice::Setup(plGLPipeline* pipe, hsWindowHndl window, hsWindowHndl device) +{ + fPipeline = pipe; + fWindow = window; + fDevice = device; +} + +bool plGLDevice::InitDevice() +{ +#ifdef USE_EGL + // The USE_EGL define tells us whether the epoxy library includes support + // for attempting to use EGL on the current platform, but we still need to + // check if EGL is actually available at runtime. + // + // On Windows, this may be true in cases like the PowerVR SDK or when using + // ANGLE. + // + // On Linux, this should be true with mesa or nvidia drivers. + if (epoxy_has_egl() && !fImpl) + fImpl = plEGLDevice::TryInit(fWindow, fDevice, fErrorMsg); +#endif + +#ifdef HS_BUILD_FOR_WIN32 + if (!fImpl) + fImpl = plWGLDevice::TryInit(fWindow, fDevice, fErrorMsg); +#endif + +#ifdef HS_BUILD_FOR_MACOS + if (!fImpl) + fImpl = plCGLDevice::TryInit(fWindow, fDevice, fErrorMsg); +#endif + + // If we still don't have a valid context type set by this point, we've + // failed to initialize so we need to exit. + if (!fImpl) + return false; + + plStatusLog::AddLineSF("pipeline.log", "Initialized with OpenGL {}", reinterpret_cast(glGetString(GL_VERSION))); + +#ifdef HS_DEBUGGING + if (plGLVersion() >= 43) { + glEnable(GL_DEBUG_OUTPUT); + + // Turn off low-severity messages + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, GL_FALSE); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, nullptr, GL_FALSE); + glDebugMessageCallback(plGLDebugLog, 0); + } +#endif + + glEnable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glEnable(GL_POLYGON_OFFSET_FILL); + glEnable(GL_CULL_FACE); + glEnable(GL_MULTISAMPLE); + glFrontFace(GL_CCW); + glCullFace(GL_BACK); + + if (plGLVersion() >= 46) + glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); + + return true; +} + +void plGLDevice::Shutdown() +{ + if (fImpl) + fImpl->Shutdown(); + + delete fImpl; } void plGLDevice::SetRenderTarget(plRenderTarget* target) { + plGLRenderTargetRef* ref = nullptr; + + if (target != nullptr) { + ref = static_cast(target->GetDeviceRef()); + + if (ref == nullptr || ref->IsDirty()) + ref = static_cast(fPipeline->MakeRenderTargetRef(target)); + } + + if (ref == nullptr) { + /// Set to main screen + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + if (plGLVersion() >= 46) + glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); + } else { + /// Set to this target + glBindFramebuffer(GL_FRAMEBUFFER, ref->fFrameBuffer); + + // We need to flip the Y axis :( + if (plGLVersion() >= 46) + glClipControl(GL_UPPER_LEFT, GL_ZERO_TO_ONE); + // else... find a way to do this with the projection matrix? + } + + SetViewport(); } void plGLDevice::SetViewport() { + glViewport(fPipeline->GetViewTransform().GetViewPortLeft(), + fPipeline->GetViewTransform().GetViewPortTop(), + fPipeline->GetViewTransform().GetViewPortWidth(), + fPipeline->GetViewTransform().GetViewPortHeight()); +} + +bool plGLDevice::BeginRender() +{ + if (fActiveThread == hsThread::ThisThreadHash()) { + return true; + } + + fActiveThread = hsThread::ThisThreadHash(); + + // Initialize OpenGL + if (!InitDevice()) { + plStatusLog::AddLineS("pipeline.log", GetErrorString()); + return false; + } + + if (fImpl) + return fImpl->BeginRender(fErrorMsg); + + return false; +} + +bool plGLDevice::EndRender() +{ + if (fPipeline->fCurrRenderTarget != nullptr) { + return true; + } + + if (fImpl) + return fImpl->EndRender(fErrorMsg); + + return false; +} + +static uint32_t IGetBufferFormatSize(uint8_t format) +{ + uint32_t size = sizeof( float ) * 6 + sizeof( uint32_t ) * 2; // Position and normal, and two packed colors + + switch (format & plGBufferGroup::kSkinWeightMask) + { + case plGBufferGroup::kSkinNoWeights: + break; + case plGBufferGroup::kSkin1Weight: + size += sizeof(float); + break; + default: + hsAssert( false, "Invalid skin weight value in IGetBufferFormatSize()" ); + } + + size += sizeof( float ) * 3 * plGBufferGroup::CalcNumUVs(format); + + return size; +} + +void plGLDevice::SetupVertexBufferRef(plGBufferGroup* owner, uint32_t idx, VertexBufferRef* vRef) +{ + uint8_t format = owner->GetVertexFormat(); + + if (format & plGBufferGroup::kSkinIndices) { + format &= ~(plGBufferGroup::kSkinWeightMask | plGBufferGroup::kSkinIndices); + format |= plGBufferGroup::kSkinNoWeights; // Should do nothing, but just in case... + vRef->SetSkinned(true); + vRef->SetVolatile(true); + } + + uint32_t vertSize = IGetBufferFormatSize(format); // vertex stride + uint32_t numVerts = owner->GetVertBufferCount(idx); + + vRef->fOwner = owner; + vRef->fCount = numVerts; + vRef->fVertexSize = vertSize; + vRef->fFormat = format; + vRef->fRefTime = 0; + + vRef->SetDirty(true); + vRef->SetRebuiltSinceUsed(true); + vRef->fData = nullptr; + + vRef->SetVolatile(vRef->Volatile() || owner->AreVertsVolatile()); + + vRef->fIndex = idx; + + owner->SetVertexBufferRef(idx, vRef); + hsRefCnt_SafeUnRef(vRef); +} + +void plGLDevice::CheckStaticVertexBuffer(VertexBufferRef* vRef, plGBufferGroup* owner, uint32_t idx) +{ + hsAssert(!vRef->Volatile(), "Creating a managed vertex buffer for a volatile buffer ref"); + + if (!vRef->fRef) { + if (plGLVersion() >= 45) { + glCreateBuffers(1, &vRef->fRef); + } else { + glGenBuffers(1, &vRef->fRef); + } + + // Fill in the vertex data. + FillStaticVertexBufferRef(vRef, owner, idx); + + // This is currently a no op, but this would let the buffer know it can + // unload the system memory copy, since we have a managed version now. + owner->PurgeVertBuffer(idx); + } +} + +void plGLDevice::FillStaticVertexBufferRef(VertexBufferRef* ref, plGBufferGroup* group, uint32_t idx) +{ + if (!ref->fRef) + // We most likely already warned about this earlier, best to just quietly return now + return; + + const uint32_t vertSize = ref->fVertexSize; + const uint32_t vertStart = group->GetVertBufferStart(idx) * vertSize; + const uint32_t size = group->GetVertBufferEnd(idx) * vertSize - vertStart; + if (!size) + return; + + + if (ref->fData) { + if (plGLVersion() >= 45) { + glNamedBufferData(ref->fRef, size, ref->fData + vertStart, GL_STATIC_DRAW); + } else { + glBindBuffer(GL_ARRAY_BUFFER, ref->fRef); + glBufferData(GL_ARRAY_BUFFER, size, ref->fData + vertStart, GL_STATIC_DRAW); + } + } else { + hsAssert(0 == vertStart, "Offsets on non-interleaved data not supported"); + hsAssert(group->GetVertBufferCount(idx) * vertSize == size, "Trailing dead space on non-interleaved data not supported"); + + uint8_t* buffer = new uint8_t[size]; + uint8_t* ptr = buffer; + const uint32_t vertSmallSize = group->GetVertexLiteStride() - sizeof(hsPoint3) * 2; + uint8_t* srcVPtr = group->GetVertBufferData(idx); + plGBufferColor* const srcCPtr = group->GetColorBufferData(idx); + + const int numCells = group->GetNumCells(idx); + for (int i = 0; i < numCells; i++) { + plGBufferCell* cell = group->GetCell(idx, i); + + if (cell->fColorStart == uint32_t(-1)) { + /// Interleaved, do straight copy + memcpy(ptr, srcVPtr + cell->fVtxStart, cell->fLength * vertSize); + ptr += cell->fLength * vertSize; + } else { + hsStatusMessage("Non interleaved data"); + + /// Separated, gotta interleave + uint8_t* tempVPtr = srcVPtr + cell->fVtxStart; + plGBufferColor* tempCPtr = srcCPtr + cell->fColorStart; + + for (int j = 0; j < cell->fLength; j++) + { + memcpy(ptr, tempVPtr, sizeof(hsPoint3) * 2); + ptr += sizeof(hsPoint3) * 2; + tempVPtr += sizeof(hsPoint3) * 2; + + memcpy(ptr, &tempCPtr->fDiffuse, sizeof(uint32_t)); + ptr += sizeof(uint32_t); + memcpy(ptr, &tempCPtr->fSpecular, sizeof(uint32_t)); + ptr += sizeof(uint32_t); + + memcpy(ptr, tempVPtr, vertSmallSize); + ptr += vertSmallSize; + tempVPtr += vertSmallSize; + tempCPtr++; + } + } + } + + hsAssert((ptr - buffer) == size, "Didn't fill the buffer?"); + if (plGLVersion() >= 45) { + glNamedBufferData(ref->fRef, size, buffer, GL_STATIC_DRAW); + } else { + glBindBuffer(GL_ARRAY_BUFFER, ref->fRef); + glBufferData(GL_ARRAY_BUFFER, size, buffer, GL_STATIC_DRAW); + } + + delete[] buffer; + } + + /// Unlock and clean up + ref->SetRebuiltSinceUsed(true); + ref->SetDirty(false); +} + +void plGLDevice::FillVolatileVertexBufferRef(VertexBufferRef* ref, plGBufferGroup* group, uint32_t idx) +{ + uint8_t* dst = ref->fData; + uint8_t* src = group->GetVertBufferData(idx); + + size_t uvChanSize = plGBufferGroup::CalcNumUVs(group->GetVertexFormat()) * sizeof(float) * 3; + uint8_t numWeights = (group->GetVertexFormat() & plGBufferGroup::kSkinWeightMask) >> 4; + + for (uint32_t i = 0; i < ref->fCount; ++i) { + memcpy(dst, src, sizeof(hsPoint3)); // pre-pos + dst += sizeof(hsPoint3); + src += sizeof(hsPoint3); + + src += numWeights * sizeof(float); // weights + + if (group->GetVertexFormat() & plGBufferGroup::kSkinIndices) + src += sizeof(uint32_t); // indices + + memcpy(dst, src, sizeof(hsVector3)); // pre-normal + dst += sizeof(hsVector3); + src += sizeof(hsVector3); + + memcpy(dst, src, sizeof(uint32_t) * 2); // diffuse & specular + dst += sizeof(uint32_t) * 2; + src += sizeof(uint32_t) * 2; + + // UVWs + memcpy(dst, src, uvChanSize); + src += uvChanSize; + dst += uvChanSize; + } +} + +void plGLDevice::SetupIndexBufferRef(plGBufferGroup* owner, uint32_t idx, IndexBufferRef* iRef) +{ + uint32_t numIndices = owner->GetIndexBufferCount(idx); + iRef->fCount = numIndices; + iRef->fOwner = owner; + iRef->fIndex = idx; + iRef->fRefTime = 0; + + iRef->SetDirty(true); + iRef->SetRebuiltSinceUsed(true); + + owner->SetIndexBufferRef(idx, iRef); + hsRefCnt_SafeUnRef(iRef); + + iRef->SetVolatile(owner->AreIdxVolatile()); +} + +void plGLDevice::CheckIndexBuffer(IndexBufferRef* iRef) +{ + if (!iRef->fRef && iRef->fCount) { + iRef->SetVolatile(false); + + if (plGLVersion() >= 45) { + glCreateBuffers(1, &iRef->fRef); + } else { + glGenBuffers(1, &iRef->fRef); + } + + iRef->SetDirty(true); + iRef->SetRebuiltSinceUsed(true); + } +} + +void plGLDevice::FillIndexBufferRef(IndexBufferRef* iRef, plGBufferGroup* owner, uint32_t idx) +{ + uint32_t startIdx = owner->GetIndexBufferStart(idx); + uint32_t size = (owner->GetIndexBufferEnd(idx) - startIdx) * sizeof(uint16_t); + + if (!size) + return; + + if (plGLVersion() >= 45) { + glNamedBufferData(iRef->fRef, size, owner->GetIndexBufferData(idx) + startIdx, GL_STATIC_DRAW); + } else { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iRef->fRef); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, owner->GetIndexBufferData(idx) + startIdx, GL_STATIC_DRAW); + } + + iRef->SetDirty(false); +} + +void plGLDevice::SetupTextureRef(plLayerInterface* layer, plBitmap* img, TextureRef* tRef) +{ + tRef->fOwner = img; + + if (img->IsCompressed()) { + switch (img->fDirectXInfo.fCompressionType) { + case plBitmap::DirectXInfo::kDXT1: + tRef->fFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + break; + case plBitmap::DirectXInfo::kDXT5: + tRef->fFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + break; + } + } else { + switch (img->fUncompressedInfo.fType) { + case plBitmap::UncompressedInfo::kRGB8888: + tRef->fFormat = GL_RGBA; + tRef->fDataType = GL_UNSIGNED_BYTE; + tRef->fDataFormat = GL_BGRA; + break; + case plBitmap::UncompressedInfo::kRGB4444: + tRef->fFormat = GL_RGBA; + tRef->fDataType = GL_UNSIGNED_SHORT_4_4_4_4; + tRef->fDataFormat = GL_BGRA; + break; + case plBitmap::UncompressedInfo::kRGB1555: + tRef->fFormat = GL_RGBA; + tRef->fDataType = GL_UNSIGNED_SHORT_5_5_5_1; + tRef->fDataFormat = GL_BGRA; + break; + case plBitmap::UncompressedInfo::kInten8: + tRef->fFormat = GL_LUMINANCE; + tRef->fDataType = GL_UNSIGNED_BYTE; + tRef->fDataFormat = GL_LUMINANCE; + break; + case plBitmap::UncompressedInfo::kAInten88: + tRef->fFormat = GL_LUMINANCE_ALPHA; + tRef->fDataType = GL_UNSIGNED_BYTE; + tRef->fDataFormat = GL_LUMINANCE_ALPHA; + break; + } + } + + tRef->SetDirty(true); + + img->SetDeviceRef(tRef); + hsRefCnt_SafeUnRef(tRef); +} + +void plGLDevice::CheckTexture(TextureRef* tRef) +{ + if (!tRef->fRef) { + glGenTextures(1, &tRef->fRef); + + tRef->SetDirty(true); + } +} + +void plGLDevice::BindTexture(TextureRef* tRef, plMipmap* img, GLuint mapping) +{ + LOG_GL_ERROR_CHECK("Bind Texture failed"); + + tRef->fLevels = img->GetNumLevels() - 1; + + if (img->IsCompressed()) { + // Hack around the smallest levels being unusable + img->SetCurrLevel(tRef->fLevels); + while ((img->GetCurrWidth() | img->GetCurrHeight()) & 0x03) { + tRef->fLevels--; + hsAssert(tRef->fLevels >= 0, "How was this ever compressed?" ); + img->SetCurrLevel(tRef->fLevels); + } + + for (GLuint lvl = 0; lvl <= tRef->fLevels; lvl++) { + img->SetCurrLevel(lvl); + + glCompressedTexImage2D(mapping, lvl, tRef->fFormat, img->GetCurrWidth(), img->GetCurrHeight(), 0, img->GetCurrLevelSize(), img->GetCurrLevelPtr()); + LOG_GL_ERROR_CHECK(ST::format("Texture Image failed at level {}", lvl)); + } + } else { + for (GLuint lvl = 0; lvl <= tRef->fLevels; lvl++) { + img->SetCurrLevel(lvl); + + glTexImage2D(mapping, lvl, tRef->fFormat, img->GetCurrWidth(), img->GetCurrHeight(), 0, tRef->fDataFormat, tRef->fDataType, img->GetCurrLevelPtr()); + LOG_GL_ERROR_CHECK(ST::format("non-DXT Texture Image \"{}\" failed at level {}", (img->GetKey() ? img->GetKeyName() : ""), lvl)); + } + } +} + +void plGLDevice::MakeTextureRef(TextureRef* tRef, plLayerInterface* layer, plMipmap* img) +{ + tRef->fMapping = GL_TEXTURE_2D; + + if (!img->GetImage()) { + glBindTexture(tRef->fMapping, 0); + return; + } + + glBindTexture(tRef->fMapping, tRef->fRef); + BindTexture(tRef, img, tRef->fMapping); + + if (plGLVersion() >= 43) { + glObjectLabel(GL_TEXTURE, tRef->fRef, -1, img->GetKeyName().c_str()); + } + + glTexParameteri(tRef->fMapping, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if (tRef->fLevels) { + glTexParameteri(tRef->fMapping, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(tRef->fMapping, GL_TEXTURE_MAX_LEVEL, tRef->fLevels); + } else { + glTexParameteri(tRef->fMapping, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + + tRef->SetDirty(false); + + LOG_GL_ERROR_CHECK(ST::format("Mipmap Texture \"{}\" failed", img->GetKeyName())); +} + +void plGLDevice::MakeCubicTextureRef(TextureRef* tRef, plLayerInterface* layer, plCubicEnvironmap* img) +{ + static const GLenum kFaceMapping[] = { + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, // kLeftFace + GL_TEXTURE_CUBE_MAP_POSITIVE_X, // kRightFace + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, // kFrontFace + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, // kBackFace + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, // kTopFace + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y // kBottomFace + }; + + tRef->fMapping = GL_TEXTURE_CUBE_MAP; + glBindTexture(tRef->fMapping, tRef->fRef); + + for (size_t i = 0; i < 6; i++) { + BindTexture(tRef, img->GetFace(i), kFaceMapping[i]); + } + + if (plGLVersion() >= 43) { + glObjectLabel(GL_TEXTURE, tRef->fRef, -1, img->GetKeyName().c_str()); + } + + glTexParameteri(tRef->fMapping, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(tRef->fMapping, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(tRef->fMapping, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(tRef->fMapping, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if (tRef->fLevels) { + glTexParameteri(tRef->fMapping, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(tRef->fMapping, GL_TEXTURE_MAX_LEVEL, tRef->fLevels); + } else { + glTexParameteri(tRef->fMapping, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + + tRef->SetDirty(false); + + LOG_GL_ERROR_CHECK(ST::format("Cubic Environ Texture \"{}\" failed", img->GetKeyName())); } void plGLDevice::SetProjectionMatrix(const hsMatrix44& src) { + hsMatrix2GL(src, fMatrixProj); } void plGLDevice::SetWorldToCameraMatrix(const hsMatrix44& src) { + hsMatrix44 inv; + src.GetInverse(&inv); + + hsMatrix2GL(src, fMatrixW2C); + hsMatrix2GL(inv, fMatrixC2W); } void plGLDevice::SetLocalToWorldMatrix(const hsMatrix44& src) { + hsMatrix44 inv; + src.GetInverse(&inv); + + hsMatrix2GL(src, fMatrixL2W); + hsMatrix2GL(inv, fMatrixW2L); } diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLDevice.h b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLDevice.h index 2de62f4cfd..ff3d553f90 100644 --- a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLDevice.h +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLDevice.h @@ -43,21 +43,61 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #define _plGLDevice_h_ #include "hsMatrix44.h" +#include "plGLDeviceRef.h" #include +class plBitmap; +class plCubicEnvironmap; class plGLPipeline; +class plLayerInterface; +class plMipmap; class plRenderTarget; +class plGLDeviceImpl +{ +protected: + hsWindowHndl fWindow; + hsWindowHndl fDevice; + + plGLDeviceImpl(hsWindowHndl window, hsWindowHndl device) + : fWindow(window), fDevice(device) {}; + +public: + virtual ~plGLDeviceImpl() { }; + + virtual void Shutdown() = 0; + virtual bool BeginRender(ST::string& error) = 0; + virtual bool EndRender(ST::string& error) = 0; +}; + class plGLDevice { +public: + typedef plGLVertexBufferRef VertexBufferRef; + typedef plGLIndexBufferRef IndexBufferRef; + typedef plGLTextureRef TextureRef; + protected: ST::string fErrorMsg; plGLPipeline* fPipeline; + plGLDeviceImpl* fImpl; + hsWindowHndl fWindow; + hsWindowHndl fDevice; + size_t fActiveThread; + GLuint fCurrentProgram; + GLfloat fMatrixL2W[16]; + GLfloat fMatrixW2L[16]; + GLfloat fMatrixW2C[16]; + GLfloat fMatrixC2W[16]; + GLfloat fMatrixProj[16]; public: plGLDevice(); + void Setup(plGLPipeline* pipe, hsWindowHndl window, hsWindowHndl device); + void Shutdown(); + /** * Set rendering to the specified render target. * @@ -66,19 +106,53 @@ class plGLDevice */ void SetRenderTarget(plRenderTarget* target); - /** Translate our viewport into a D3D viewport. */ + /** Translate our viewport into a GL viewport. */ void SetViewport(); + bool BeginRender(); + + /** + * Tell GL we're through rendering for this frame, and flip the back buffer + * to front. + */ + bool EndRender(); + + /* Device Ref Functions **************************************************/ + void SetupVertexBufferRef(plGBufferGroup* owner, uint32_t idx, VertexBufferRef* vRef); + void CheckStaticVertexBuffer(VertexBufferRef* vRef, plGBufferGroup* owner, uint32_t idx); + void FillStaticVertexBufferRef(VertexBufferRef* ref, plGBufferGroup* group, uint32_t idx); + void FillVolatileVertexBufferRef(VertexBufferRef* ref, plGBufferGroup* group, uint32_t idx); + void SetupIndexBufferRef(plGBufferGroup* owner, uint32_t idx, IndexBufferRef* iRef); + void CheckIndexBuffer(IndexBufferRef* iRef); + void FillIndexBufferRef(IndexBufferRef* iRef, plGBufferGroup* owner, uint32_t idx); + void SetupTextureRef(plLayerInterface* layer, plBitmap* img, TextureRef* tRef); + void CheckTexture(TextureRef* tRef); + void MakeTextureRef(TextureRef* tRef, plLayerInterface* layer, plMipmap* img); + void MakeCubicTextureRef(TextureRef* tRef, plLayerInterface* layer, plCubicEnvironmap* img); void SetProjectionMatrix(const hsMatrix44& src); void SetWorldToCameraMatrix(const hsMatrix44& src); void SetLocalToWorldMatrix(const hsMatrix44& src); - struct VertexBufferRef; - struct IndexBufferRef; - struct TextureRef; + void SetCurrentProgram(GLuint program) { fCurrentProgram = program; } ST::string GetErrorString() const { return fErrorMsg; } + + bool HasContext() const { return fImpl != nullptr; } + + const GLfloat* GetL2WMatrix() const { return fMatrixL2W; } + const GLfloat* GetW2LMatrix() const { return fMatrixW2L; } + const GLfloat* GetC2WMatrix() const { return fMatrixC2W; } + const GLfloat* GetW2CMatrix() const { return fMatrixW2C; } + const GLfloat* GetProjectionMatrix() const { return fMatrixProj; } + +private: + /** + * Initializes the OpenGL rendering context. + */ + bool InitDevice(); + + void BindTexture(TextureRef* tRef, plMipmap* img, GLuint mapping); }; #endif diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLDeviceRef.h b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLDeviceRef.h index c1d4e4f7bf..a404e94bfd 100644 --- a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLDeviceRef.h +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLDeviceRef.h @@ -47,11 +47,43 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include +inline int plGLVersion() +{ + // This exists for testing purposes to force the pipeline to behave as if a + // specific version of OpenGL were present, mainly for ensuring + // compatibility with older GL API versions on machines where newer + // versions are available by default. To pretend to be limited to a + // specific version, just return the GL version with the decimal removed as + // an integer: + + // return 42; // Pretend we only support OpenGL 4.2 + return epoxy_gl_version(); +} + +// Helper macro for logging GL Errors +#ifdef HS_DEBUGGING +# include "plStatusLog/plStatusLog.h" +# define LOG_GL_ERROR_CHECK(message) \ + do { \ + GLenum e; \ + if ((e = glGetError()) != GL_NO_ERROR) { \ + plStatusLog::AddLineSF("pipeline.log", "{}: {}", message, uint32_t(e)); \ + } \ + } while(0); +#else +# define LOG_GL_ERROR_CHECK(message) +#endif + +class plBitmap; +class plGBufferGroup; +class plRenderTarget; + class plGLDeviceRef : public hsGDeviceRef { -protected: - GLint fRef; +public: + GLuint fRef; +protected: plGLDeviceRef* fNext; plGLDeviceRef** fBack; @@ -61,13 +93,140 @@ class plGLDeviceRef : public hsGDeviceRef plGLDeviceRef* GetNext() { return fNext; } bool IsLinked() { return fBack != nullptr; } - virtual void Release() = 0; + bool HasFlag(uint32_t f) const { return 0 != (fFlags & f); } + void SetFlag(uint32_t f, bool on) { if(on) fFlags |= f; else fFlags &= ~f; } - plGLDeviceRef(); + virtual void Release() = 0; + plGLDeviceRef(); virtual ~plGLDeviceRef(); }; +class plGLVertexBufferRef : public plGLDeviceRef +{ +public: + uint32_t fCount; + uint32_t fIndex; + uint32_t fVertexSize; + int32_t fOffset; + uint8_t fFormat; + + plGBufferGroup* fOwner; + uint8_t* fData; + + uint32_t fRefTime; + + enum { + kRebuiltSinceUsed = 0x10, // kDirty = 0x1 is in hsGDeviceRef + kVolatile = 0x20, + kSkinned = 0x40 + }; + + + bool RebuiltSinceUsed() const { return HasFlag(kRebuiltSinceUsed); } + void SetRebuiltSinceUsed(bool b) { SetFlag(kRebuiltSinceUsed, b); } + + bool Volatile() const { return HasFlag(kVolatile); } + void SetVolatile(bool b) { SetFlag(kVolatile, b); } + + bool Skinned() const { return HasFlag(kSkinned); } + void SetSkinned(bool b) { SetFlag(kSkinned, b); } + + bool Expired(uint32_t t) const { return Volatile() && (IsDirty() || (fRefTime != t)); } + void SetRefTime(uint32_t t) { fRefTime = t; } + + + void Link(plGLVertexBufferRef** back ) { plGLDeviceRef::Link((plGLDeviceRef**)back); } + plGLVertexBufferRef* GetNext() { return (plGLVertexBufferRef*)fNext; } + + + plGLVertexBufferRef() + : plGLDeviceRef(), fCount(), fIndex(), fVertexSize(), fOffset(), + fFormat(), fOwner(), fData(), fRefTime() + {} + + virtual ~plGLVertexBufferRef(); + void Release() override; +}; + + +class plGLIndexBufferRef : public plGLDeviceRef +{ +public: + uint32_t fCount; + uint32_t fIndex; + int32_t fOffset; + plGBufferGroup* fOwner; + uint32_t fRefTime; + + enum { + kRebuiltSinceUsed = 0x10, // kDirty = 0x1 is in hsGDeviceRef + kVolatile = 0x20 + }; + + + bool RebuiltSinceUsed() const { return HasFlag(kRebuiltSinceUsed); } + void SetRebuiltSinceUsed(bool b) { SetFlag(kRebuiltSinceUsed, b); } + + bool Volatile() const { return HasFlag(kVolatile); } + void SetVolatile(bool b) { SetFlag(kVolatile, b); } + + bool Expired(uint32_t t) const { return Volatile() && (IsDirty() || (fRefTime != t)); } + void SetRefTime(uint32_t t) { fRefTime = t; } + + + void Link(plGLIndexBufferRef** back) { plGLDeviceRef::Link((plGLDeviceRef**)back); } + plGLIndexBufferRef* GetNext() { return (plGLIndexBufferRef*)fNext; } + + + plGLIndexBufferRef() + : plGLDeviceRef(), fCount(), fIndex(), fOffset(), fOwner(), fRefTime() + {} + + virtual ~plGLIndexBufferRef(); + void Release() override; +}; + + +class plGLTextureRef : public plGLDeviceRef +{ +public: + plBitmap* fOwner; + uint32_t fLevels; + GLuint fMapping; + GLuint fFormat; + GLuint fDataType; + GLuint fDataFormat; + + void Link(plGLTextureRef** back) { plGLDeviceRef::Link((plGLDeviceRef**)back); } + plGLTextureRef* GetNext() { return (plGLTextureRef*)fNext; } + + plGLTextureRef() + : plGLDeviceRef(), fOwner(), fLevels(1), fMapping(), fFormat(), fDataType(), fDataFormat() + {} + + virtual ~plGLTextureRef(); + void Release() override; +}; + + +class plGLRenderTargetRef: public plGLTextureRef +{ +public: + // fRef is the texture ref, so we can keep using this like a normal texture + GLuint fFrameBuffer; + GLuint fDepthBuffer; + + void Link(plGLRenderTargetRef**back) { plGLDeviceRef::Link((plGLDeviceRef**)back); } + plGLRenderTargetRef* GetNext() { return (plGLRenderTargetRef*)fNext; } + + virtual ~plGLRenderTargetRef(); + + void Release() override; + + virtual void SetOwner(plRenderTarget* targ) { fOwner = (plBitmap*)targ; } +}; + #endif // _plGLDeviceRef_inc_ diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLDeviceRefs.cpp b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLDeviceRefs.cpp index 3302118a09..21330c3672 100644 --- a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLDeviceRefs.cpp +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLDeviceRefs.cpp @@ -39,27 +39,23 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com Mead, WA 99021 *==LICENSE==*/ -#include "plPipeline/hsWinRef.h" #include "plGLPipeline.h" #include "plGLDeviceRef.h" #include "plProfile.h" -#include "plStatusLog/plStatusLog.h" -plProfile_CreateMemCounter("Vertices", "Memory", MemVertex); -plProfile_CreateMemCounter("Indices", "Memory", MemIndex); -plProfile_CreateMemCounter("Textures", "Memory", MemTexture); +plProfile_Extern(MemVertex); +plProfile_Extern(MemIndex); +plProfile_Extern(MemTexture); /***************************************************************************** ** Generic plGLDeviceRef Functions ** *****************************************************************************/ plGLDeviceRef::plGLDeviceRef() -{ - fNext = nullptr; - fBack = nullptr; -} + : fRef(), fNext(), fBack() +{ } plGLDeviceRef::~plGLDeviceRef() { @@ -89,3 +85,83 @@ void plGLDeviceRef::Link(plGLDeviceRef** back) fBack = back; *back = this; } + + +/***************************************************************************** + ** Vertex buffer cleanup Functions ** + *****************************************************************************/ + +plGLVertexBufferRef::~plGLVertexBufferRef() +{ + Release(); +} + + +void plGLVertexBufferRef::Release() +{ + if (fRef) { + glDeleteBuffers(1, &fRef); + fRef = 0; + } + + if (fData) { + delete[] fData; + } + + SetDirty(true); +} + + +/***************************************************************************** + ** Index buffer cleanup Functions ** + *****************************************************************************/ + +plGLIndexBufferRef::~plGLIndexBufferRef() +{ + Release(); +} + +void plGLIndexBufferRef::Release() +{ + if (fRef) { + glDeleteBuffers(1, &fRef); + fRef = 0; + } + SetDirty(true); +} + + +/***************************************************************************** + ** Texture Reference cleanup Functions ** + *****************************************************************************/ + +plGLTextureRef::~plGLTextureRef() +{ + Release(); +} + + +void plGLTextureRef::Release() +{ + if (fRef) { + glDeleteTextures(1, &fRef); + fRef = 0; + } + SetDirty(true); +} + + +/***************************************************************************** + ** FrameBuffer cleanup Functions ** + *****************************************************************************/ + +plGLRenderTargetRef::~plGLRenderTargetRef() +{ + Release(); +} + +void plGLRenderTargetRef::Release() +{ + plGLTextureRef::Release(); + SetDirty(true); +} diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLEnumerate.cpp b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLEnumerate.cpp index 55a0d076ca..3d98dfafdd 100644 --- a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLEnumerate.cpp +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLEnumerate.cpp @@ -46,21 +46,12 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include #include "plGLPipeline.h" +#include "plCGLDevice.h" +#include "plEGLDevice.h" +#include "plWGLDevice.h" -bool fillDeviceRecord(hsG3DDeviceRecord& devRec) +static void fillDeviceRecord(hsG3DDeviceRecord& devRec) { - if (epoxy_gl_version() < 33) - return false; - - const char* renderer = reinterpret_cast(glGetString(GL_RENDERER)); - devRec.SetDeviceDesc(renderer); - - const char* vendor = reinterpret_cast(glGetString(GL_VENDOR)); - devRec.SetDriverDesc(vendor); - - const char* version = reinterpret_cast(glGetString(GL_VERSION)); - devRec.SetDriverVersion(version); - devRec.SetCap(hsG3DDeviceSelector::kCapsMipmap); devRec.SetCap(hsG3DDeviceSelector::kCapsPerspective); devRec.SetCap(hsG3DDeviceSelector::kCapsCompressTextures); @@ -74,210 +65,8 @@ bool fillDeviceRecord(hsG3DDeviceRecord& devRec) devMode.SetHeight(hsG3DDeviceSelector::kDefaultHeight); devMode.SetColorDepth(hsG3DDeviceSelector::kDefaultDepth); devRec.GetModes().emplace_back(devMode); - - return true; } - -#ifdef USE_EGL -#include - -void plEGLEnumerate(std::vector& records) -{ - EGLDisplay display = EGL_NO_DISPLAY; - EGLContext context = EGL_NO_CONTEXT; - EGLSurface surface = EGL_NO_SURFACE; - - do { - display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (display == EGL_NO_DISPLAY) - break; - - if (!eglInitialize(display, nullptr, nullptr)) - break; - - if (!eglBindAPI(EGL_OPENGL_API)) - break; - - GLint numConfigs = 0; - if (!eglGetConfigs(display, nullptr, 0, &numConfigs) || numConfigs == 0) - break; - - std::vector configs(numConfigs); - if (!eglGetConfigs(display, configs.data(), configs.size(), &numConfigs)) - break; - - EGLint ctx_attrs[] = { - EGL_CONTEXT_MAJOR_VERSION, 3, - EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, - EGL_NONE - }; - - context = eglCreateContext(display, configs[0], EGL_NO_CONTEXT, ctx_attrs); - if (context == EGL_NO_CONTEXT) - break; - - surface = eglCreatePbufferSurface(display, configs[0], nullptr); - if (surface == EGL_NO_SURFACE) - break; - - if (!eglMakeCurrent(display, surface, surface, context)) - break; - - hsG3DDeviceRecord devRec; - devRec.SetG3DDeviceType(hsG3DDeviceSelector::kDevTypeOpenGL); - devRec.SetDriverName("EGL"); - - if (fillDeviceRecord(devRec)) - records.emplace_back(devRec); - } while (0); - - // Cleanup: - if (surface != EGL_NO_SURFACE) { - eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglDestroySurface(display, surface); - } - - if (context != EGL_NO_CONTEXT) - eglDestroyContext(display, context); - - if (display != EGL_NO_DISPLAY) - eglTerminate(display); -} -#endif // USE_EGL - - -#ifdef HS_BUILD_FOR_WIN32 -#include "hsWindows.h" -#include - -void plWGLEnumerate(std::vector& records) -{ - ATOM cls = 0; - HWND wnd = nullptr; - HDC dc = nullptr; - HGLRC ctx = nullptr; - - do { - WNDCLASSW tempClass = {}; - tempClass.lpfnWndProc = DefWindowProc; - tempClass.hInstance = GetModuleHandle(nullptr); - tempClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); - tempClass.lpszClassName = L"GLTestClass"; - tempClass.style = CS_OWNDC; - - cls = RegisterClassW(&tempClass); - if (!cls) - break; - - wnd = CreateWindowExW(WS_EX_NOACTIVATE, reinterpret_cast(cls), - L"OpenGL Test Window", - WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - nullptr, nullptr, GetModuleHandle(nullptr), nullptr); - if (!wnd) - break; - - dc = GetDC(wnd); - if (!dc) - break; - - PIXELFORMATDESCRIPTOR pfd = {}; - pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); - pfd.nVersion = 1; - pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_GENERIC_ACCELERATED | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; - pfd.iPixelType = PFD_TYPE_RGBA; - pfd.cColorBits = 24; - pfd.cDepthBits = 24; - pfd.iLayerType = PFD_MAIN_PLANE; - - int format = ChoosePixelFormat(dc, &pfd); - if (!format) - break; - - if (!SetPixelFormat(dc, format, &pfd)) - break; - - ctx = wglCreateContext(dc); - if (!ctx) - break; - - if (!wglMakeCurrent(dc, ctx)) - break; - - hsG3DDeviceRecord devRec; - devRec.SetG3DDeviceType(hsG3DDeviceSelector::kDevTypeOpenGL); - devRec.SetDriverName("opengl32.dll"); - - if (fillDeviceRecord(devRec)) - records.emplace_back(devRec); - } while (0); - - // Cleanup: - if (ctx) { - wglMakeCurrent(nullptr, nullptr); - wglDeleteContext(ctx); - } - - if (dc) - ReleaseDC(wnd, dc); - - if (wnd) - DestroyWindow(wnd); - - if (cls) - UnregisterClassW(reinterpret_cast(cls), GetModuleHandle(nullptr)); -} -#endif // HS_BUILD_FOR_WIN32 - - -#ifdef HS_BUILD_FOR_MACOS -#include - -void plCGLEnumerate(std::vector& records) -{ - IGNORE_WARNINGS_BEGIN("deprecated-declarations") - CGLPixelFormatObj pix = nullptr; - CGLContextObj ctx = nullptr; - - do { - CGLPixelFormatAttribute attribs[6] = { - kCGLPFAAccelerated, - kCGLPFANoRecovery, - kCGLPFADoubleBuffer, - kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core, - (CGLPixelFormatAttribute) 0 - }; - - int nPix = 0; - if (CGLChoosePixelFormat(attribs, &pix, &nPix) != kCGLNoError || nPix == 0) - break; - - if (CGLCreateContext(pix, nullptr, &ctx) != kCGLNoError) - break; - - CGLSetCurrentContext(ctx); - - hsG3DDeviceRecord devRec; - devRec.SetG3DDeviceType(hsG3DDeviceSelector::kDevTypeOpenGL); - devRec.SetDriverName("OpenGL.framework"); - - if (fillDeviceRecord(devRec)) - records.emplace_back(devRec); - } while (0); - - // Cleanup: - if (ctx) { - CGLSetCurrentContext(nullptr); - CGLReleaseContext(ctx); - } - - if (pix) - CGLReleasePixelFormat(pix); - IGNORE_WARNINGS_END -} -#endif // HS_BUILD_FOR_MACOS - void plGLEnumerate::Enumerate(std::vector& records) { #ifdef USE_EGL @@ -290,15 +79,28 @@ void plGLEnumerate::Enumerate(std::vector& records) // // On Linux, this should be true with mesa or nvidia drivers. if (epoxy_has_egl()) { - plEGLEnumerate(records); + hsG3DDeviceRecord rec; + fillDeviceRecord(rec); + if (plEGLDevice::Enumerate(rec)) + records.emplace_back(rec); } #endif #ifdef HS_BUILD_FOR_WIN32 - plWGLEnumerate(records); + { + hsG3DDeviceRecord rec; + fillDeviceRecord(rec); + if (plWGLDevice::Enumerate(rec)) + records.emplace_back(rec); + } #endif #ifdef HS_BUILD_FOR_MACOS - plCGLEnumerate(records); + { + hsG3DDeviceRecord rec; + fillDeviceRecord(rec); + if (plCGLDevice::Enumerate(rec)) + records.emplace_back(rec); + } #endif } diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLMaterialShaderRef.cpp b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLMaterialShaderRef.cpp new file mode 100644 index 0000000000..452f730b37 --- /dev/null +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLMaterialShaderRef.cpp @@ -0,0 +1,636 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "plGLMaterialShaderRef.h" +#include "plGLDevice.h" +#include "plGLPipeline.h" + +#include +#include +#include + +#include "HeadSpin.h" +#include "hsBitVector.h" + +#include "hsGMatState.inl" +#include "plPipeDebugFlags.h" + +#include "plDrawable/plGBufferGroup.h" +#include "plGImage/plCubicEnvironmap.h" +#include "plGImage/plMipmap.h" +#include "plPipeline/plCubicRenderTarget.h" +#include "plPipeline/plRenderTarget.h" +#include "plStatusLog/plStatusLog.h" +#include "plSurface/hsGMaterial.h" +#include "plSurface/plLayerInterface.h" + +// From plGLDevice.cpp +extern GLfloat* hsMatrix2GL(const hsMatrix44& src, GLfloat* dst); + +const char* VERTEX_SHADER_STRING = R"(#version 330 +precision lowp int; + +struct lightSource { + vec4 position; + vec4 ambient; + vec4 diffuse; + vec4 specular; + vec3 direction; + vec3 spotProps; // (falloff, theta, phi) + float constAtten; + float linAtten; + float quadAtten; + float scale; +}; + + +// Input Attributes +layout(location = 0) in vec3 aVtxPosition; +layout(location = 1) in vec3 aVtxNormal; +layout(location = 2) in vec4 aVtxColor; +layout(location = 3) in vec3 aVtxUVWSrc[8]; + +uniform mat4 uMatrixL2W; +uniform mat4 uMatrixW2L; +uniform mat4 uMatrixW2C; +uniform mat4 uMatrixProj; + +uniform vec4 uGlobalAmb; +uniform vec4 uAmbientCol; +uniform float uAmbientSrc; +uniform vec4 uDiffuseCol; +uniform float uDiffuseSrc; +uniform vec4 uEmissiveCol; +uniform float uEmissiveSrc; +uniform vec4 uSpecularCol; +uniform float uSpecularSrc; + +uniform float uInvertVtxAlpha; // Effectively boolean, 0.0 or 1.0 + +uniform lightSource uLampSources[8]; + +// Varying outputs +out vec4 vCamPosition; +out vec4 vCamNormal; +out vec4 vVtxColor; +out vec3 vVtxUVWSrc[8]; + +void main() { + vec4 MAmbient = mix(aVtxColor.bgra, uAmbientCol, uAmbientSrc); + vec4 MDiffuse = mix(aVtxColor.bgra, uDiffuseCol, uDiffuseSrc); + vec4 MEmissive = mix(aVtxColor.bgra, uEmissiveCol, uEmissiveSrc); + vec4 MSpecular = mix(aVtxColor.bgra, uSpecularCol, uSpecularSrc); + + vec4 LAmbient = vec4(0.0, 0.0, 0.0, 0.0); + vec4 LDiffuse = vec4(0.0, 0.0, 0.0, 0.0); + + vec3 Ndirection = normalize(mat3(uMatrixW2L) * aVtxNormal); + + for (int i = 0; i < 8; i++) { + vVtxUVWSrc[i] = aVtxUVWSrc[i]; + + float attenuation; + vec3 direction; + + if (uLampSources[i].position.w == 0.0) { + // Directional Light with no attenuation + direction = -uLampSources[i].direction; + attenuation = 1.0; + } else { + // Omni Light in all directions + vec3 v2l = uLampSources[i].position.xyz - vec3(uMatrixL2W * vec4(aVtxPosition, 1.0)); + float distance = length(v2l); + direction = normalize(v2l); + + attenuation = 1.0 / (uLampSources[i].constAtten + uLampSources[i].linAtten * distance + uLampSources[i].quadAtten * pow(distance, 2.0)); + + if (uLampSources[i].spotProps.x > 0.0) { + // Spot Light with cone falloff + float a = dot(direction, normalize(-uLampSources[i].direction)); + float theta = uLampSources[i].spotProps.y; + float phi = uLampSources[i].spotProps.z; + float i = pow((a - phi) / (theta - phi), uLampSources[i].spotProps.x); + + attenuation *= clamp(i, 0.0, 1.0); + } + } + + LAmbient.rgb = LAmbient.rgb + attenuation * (uLampSources[i].ambient.rgb * uLampSources[i].scale); + LDiffuse.rgb = LDiffuse.rgb + MDiffuse.rgb * (uLampSources[i].diffuse.rgb * uLampSources[i].scale) * max(0.0, dot(Ndirection, direction)) * attenuation; + } + + vec4 ambient = clamp(MAmbient * (uGlobalAmb + LAmbient), 0.0, 1.0); + vec4 diffuse = clamp(LDiffuse, 0.0, 1.0); + vec4 material = clamp(ambient + diffuse + MEmissive, 0.0, 1.0); + + vVtxColor = vec4(material.rgb, abs(uInvertVtxAlpha - MDiffuse.a)); + + vCamPosition = uMatrixW2C * (uMatrixL2W * vec4(aVtxPosition, 1.0)); + vCamNormal = uMatrixW2C * (uMatrixL2W * vec4(aVtxNormal, 0.0)); + + gl_Position = uMatrixProj * vCamPosition; +})"; + + +const char* FRAGMENT_SHADER_STRING = R"(#version 330 +precision mediump float; +precision lowp int; + +uniform mat4 uLayerMat0; + +uniform mat4 uLayerMat[8]; +uniform int uLayerUVWSrc[8]; +uniform int uStartingLayer; +uniform int uLayersAtOnce; +uniform int uPassNumber; + +uniform sampler2D uTexture0; + +uniform float uAlphaThreshold; +uniform int uFogExponential; +uniform highp vec2 uFogValues; +uniform vec3 uFogColor; + +// Varying inputs +in highp vec4 vCamPosition; +in highp vec4 vCamNormal; +in vec4 vVtxColor; +in highp vec3 vVtxUVWSrc[8]; + +// Rendered outputs +out vec4 fragColor; + +void main() { + float baseAlpha; + vec4 coords0; + vec4 image0; + float currAlpha; + vec3 currColor; + + baseAlpha = vVtxColor.a; + coords0 = uLayerMat0 * vec4(vVtxUVWSrc[0], 1.0); + image0 = texture(uTexture0, coords0.xy); + currColor = image0.rgb; + currAlpha = image0.a * baseAlpha; + currColor = vVtxColor.rgb * currColor; + + if (currAlpha < uAlphaThreshold) { discard; }; + + highp float fogFactor = 1.0; + if (uFogExponential > 0) { + fogFactor = exp(-pow(uFogValues.y * length(vCamPosition.xyz), uFogValues.x)); + } else { + if (uFogValues.y > 0.0) { + highp float start = uFogValues.x; + highp float end = uFogValues.y; + fogFactor = (end - length(vCamPosition.xyz)) / (end - start); + } + } + + currColor = mix(currColor, uFogColor, 1.0 - clamp(fogFactor, 0.0, 1.0)); + + fragColor = vec4(currColor, currAlpha); +})"; + +plGLMaterialShaderRef::plGLMaterialShaderRef(hsGMaterial* mat, plGLPipeline* pipe) + : plGLDeviceRef(), fMaterial(mat), fPipeline(pipe), fVertShaderRef(0), fFragShaderRef(0) +{ + ILoopOverLayers(); + ICompile(); + ISetShaderVariableLocs(); +} + +plGLMaterialShaderRef::~plGLMaterialShaderRef() +{ + Release(); +} + +void plGLMaterialShaderRef::Release() +{ + if (fVertShaderRef) { + glDeleteShader(fVertShaderRef); + fVertShaderRef = 0; + } + + if (fFragShaderRef) { + glDeleteShader(fFragShaderRef); + fFragShaderRef = 0; + } + + if (fRef) { + glDeleteProgram(fRef); + fRef = 0; + } + + SetDirty(true); +} + +void plGLMaterialShaderRef::SetupTextureRefs() +{ + int32_t numTextures = 0; + + for (size_t i = 0; i < fMaterial->GetNumLayers(); i++) { + plLayerInterface* layer = fMaterial->GetLayer(i); + if (!layer) + continue; + + layer = fPipeline->IPushOverAllLayer(layer); + + // Load the image + plBitmap* img = plBitmap::ConvertNoRef(layer->GetTexture()); + + if (!img) { + layer = fPipeline->IPopOverAllLayer(layer); + continue; + } + + fPipeline->CheckTextureRef(layer); + + plGLTextureRef* texRef = static_cast(img->GetDeviceRef()); + + if (!texRef->fRef) { + layer = fPipeline->IPopOverAllLayer(layer); + continue; + } + + LOG_GL_ERROR_CHECK("PRE-Active Texture failed") + + glActiveTexture(GL_TEXTURE0 + numTextures); + LOG_GL_ERROR_CHECK("Active Texture failed") + + glBindTexture(texRef->fMapping, texRef->fRef); + LOG_GL_ERROR_CHECK("Bind Texture failed"); + + if (texRef->fMapping == GL_TEXTURE_2D) { + // Ewww, but the same texture might be used by multiple layers with different clamping flags :( + switch (layer->GetClampFlags()) { + case hsGMatState::kClampTextureU: + glTexParameteri(texRef->fMapping, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texRef->fMapping, GL_TEXTURE_WRAP_T, GL_REPEAT); + break; + case hsGMatState::kClampTextureV: + glTexParameteri(texRef->fMapping, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(texRef->fMapping, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + break; + case hsGMatState::kClampTexture: + glTexParameteri(texRef->fMapping, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texRef->fMapping, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + break; + default: + glTexParameteri(texRef->fMapping, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(texRef->fMapping, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + } + + if (this->uLayerMat[i] != -1) { + GLfloat matrix[16]; + glUniformMatrix4fv(this->uLayerMat[i], 1, GL_TRUE, hsMatrix2GL(layer->GetTransform(), matrix)); + } + + if (this->uTexture[i] != -1) + glUniform1i(this->uTexture[i], numTextures); + LOG_GL_ERROR_CHECK("Uniform Texture failed") + + layer = fPipeline->IPopOverAllLayer(layer); + numTextures++; + } +} + + +void plGLMaterialShaderRef::ICompile() +{ + LOG_GL_ERROR_CHECK("Begin Compile failed") + + int32_t numTextures = 0; + hsBitVector usedUVWs; + + for (size_t i = 0; i < fMaterial->GetNumLayers(); i++) { + plLayerInterface* layer = fMaterial->GetLayer(i); + if (!layer) + continue; + + fPipeline->CheckTextureRef(layer); + LOG_GL_ERROR_CHECK(ST::format("Check Texture Ref on layer \"{}\" failed", layer->GetKeyName())); + } + + const char* vs_code = VERTEX_SHADER_STRING; + const char* fs_code = FRAGMENT_SHADER_STRING; + + static GLuint vshader = 0; + if (!vshader) { + vshader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vshader, 1, &vs_code, nullptr); + glCompileShader(vshader); + LOG_GL_ERROR_CHECK("Vertex Shader compile failed"); + +#ifdef HS_DEBUGGING + { + GLint compiled = 0; + glGetShaderiv(vshader, GL_COMPILE_STATUS, &compiled); + if (compiled == 0) { + GLint length = 0; + glGetShaderiv(vshader, GL_INFO_LOG_LENGTH, &length); + if (length) { + char* log = new char[length]; + glGetShaderInfoLog(vshader, length, &length, log); + hsStatusMessage(log); + delete[] log; + } + } + } +#endif + } + fVertShaderRef = vshader; + + fFragShaderRef = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fFragShaderRef, 1, &fs_code, nullptr); + glCompileShader(fFragShaderRef); + LOG_GL_ERROR_CHECK("Fragment Shader compile failed"); + +#ifdef HS_DEBUGGING + { + GLint compiled = 0; + glGetShaderiv(fFragShaderRef, GL_COMPILE_STATUS, &compiled); + if (compiled == 0) { + GLint length = 0; + glGetShaderiv(fFragShaderRef, GL_INFO_LOG_LENGTH, &length); + if (length) { + char* log = new char[length]; + glGetShaderInfoLog(fFragShaderRef, length, &length, log); + hsStatusMessage(log); + delete[] log; + } + } + } +#endif + + fRef = glCreateProgram(); + LOG_GL_ERROR_CHECK("Create Program failed"); + + if (plGLVersion() >= 43) { + ST::string name = ST::format("hsGMaterial::{}", fMaterial->GetKeyName()); + glObjectLabel(GL_PROGRAM, fRef, strlen(name.c_str()), name.c_str()); + } + + glAttachShader(fRef, fVertShaderRef); + LOG_GL_ERROR_CHECK("Attach Vertex Shader failed") + + glAttachShader(fRef, fFragShaderRef); + LOG_GL_ERROR_CHECK("Attach Fragment Shader failed") +} + + +void plGLMaterialShaderRef::ISetShaderVariableLocs() +{ + glLinkProgram(fRef); + LOG_GL_ERROR_CHECK("Program Link failed"); + + uPassNumber = glGetUniformLocation(fRef, "uPassNumber"); + uAlphaThreshold = glGetUniformLocation(fRef, "uAlphaThreshold"); + uInvertVtxAlpha = glGetUniformLocation(fRef, "uInvertVtxAlpha"); + + // Matrix inputs + uMatrixProj = glGetUniformLocation(fRef, "uMatrixProj"); + uMatrixC2W = glGetUniformLocation(fRef, "uMatrixC2W"); + uMatrixW2C = glGetUniformLocation(fRef, "uMatrixW2C"); + uMatrixL2W = glGetUniformLocation(fRef, "uMatrixL2W"); + uMatrixW2L = glGetUniformLocation(fRef, "uMatrixW2L"); + + // Material inputs + uGlobalAmbient = glGetUniformLocation(fRef, "uGlobalAmb"); + uMatAmbientCol = glGetUniformLocation(fRef, "uAmbientCol"); + uMatAmbientSrc = glGetUniformLocation(fRef, "uAmbientSrc"); + uMatDiffuseCol = glGetUniformLocation(fRef, "uDiffuseCol"); + uMatDiffuseSrc = glGetUniformLocation(fRef, "uDiffuseSrc"); + uMatEmissiveCol = glGetUniformLocation(fRef, "uEmissiveCol"); + uMatEmissiveSrc = glGetUniformLocation(fRef, "uEmissiveSrc"); + uMatSpecularCol = glGetUniformLocation(fRef, "uSpecularCol"); + uMatSpecularSrc = glGetUniformLocation(fRef, "uSpecularSrc"); + + // Fog inputs + uFogExponential = glGetUniformLocation(fRef, "uFogExponential"); + uFogValues = glGetUniformLocation(fRef, "uFogValues"); + uFogColor = glGetUniformLocation(fRef, "uFogColor"); + + // Lamp inputs + for (size_t i = 0; i < 8; i++) { + uLampSources[i].position = glGetUniformLocation(fRef, ST::format("uLampSources[{}].position", i).c_str()); + uLampSources[i].ambient = glGetUniformLocation(fRef, ST::format("uLampSources[{}].ambient", i).c_str()); + uLampSources[i].diffuse = glGetUniformLocation(fRef, ST::format("uLampSources[{}].diffuse", i).c_str()); + uLampSources[i].specular = glGetUniformLocation(fRef, ST::format("uLampSources[{}].specular", i).c_str()); + uLampSources[i].direction = glGetUniformLocation(fRef, ST::format("uLampSources[{}].direction", i).c_str()); + uLampSources[i].spotProps = glGetUniformLocation(fRef, ST::format("uLampSources[{}].spotProps", i).c_str()); + uLampSources[i].constAtten = glGetUniformLocation(fRef, ST::format("uLampSources[{}].constAtten", i).c_str()); + uLampSources[i].linAtten = glGetUniformLocation(fRef, ST::format("uLampSources[{}].linAtten", i).c_str()); + uLampSources[i].quadAtten = glGetUniformLocation(fRef, ST::format("uLampSources[{}].quadAtten", i).c_str()); + uLampSources[i].scale = glGetUniformLocation(fRef, ST::format("uLampSources[{}].scale", i).c_str()); + } + + size_t layerCount = fMaterial->GetNumLayers(); + + uLayerMat.assign(layerCount, -1); + for (size_t i = 0; i < layerCount; i++) { + ST::string name = ST::format("uLayerMat{}", i); + uLayerMat[i] = glGetUniformLocation(fRef, name.c_str()); + } + + uTexture.assign(layerCount, -1); + for (size_t i = 0; i < layerCount; i++) { + ST::string name = ST::format("uTexture{}", i); + uTexture[i] = glGetUniformLocation(fRef, name.c_str()); + } +} + + +void plGLMaterialShaderRef::ILoopOverLayers() +{ + for (size_t j = 0; j < fMaterial->GetNumLayers(); ) { + size_t iCurrMat = j; + + j = IHandleMaterial(iCurrMat); + + if (j == -1) + break; + + fPassIndices.push_back(iCurrMat); + } +} + + +const hsGMatState plGLMaterialShaderRef::ICompositeLayerState(plLayerInterface* layer) +{ + hsGMatState state; + state.Composite(layer->GetState(), fPipeline->GetMaterialOverride(true), fPipeline->GetMaterialOverride(false)); + return state; +} + + +uint32_t plGLMaterialShaderRef::IHandleMaterial(uint32_t layer) +{ + if (!fMaterial || layer >= fMaterial->GetNumLayers() || !fMaterial->GetLayer(layer)) + return -1; + + if (false /*ISkipBumpMap(fMaterial, layer)*/) + return -1; + + // Ignoring the bit about ATI Radeon and UVW limits + + if (fPipeline->IsDebugFlagSet(plPipeDbg::kFlagNoDecals) && (fMaterial->GetCompositeFlags() & hsGMaterial::kCompDecal)) + return -1; + + // Ignoring the bit about self-rendering cube maps + + plLayerInterface* currLay = fPipeline->IPushOverBaseLayer(fMaterial->GetLayer(layer)); + + if (fPipeline->IsDebugFlagSet(plPipeDbg::kFlagBumpW) && (currLay->GetMiscFlags() & hsGMatState::kMiscBumpDu)) + currLay = fMaterial->GetLayer(++layer); + + currLay = fPipeline->IPushOverAllLayer(currLay); + + hsGMatState state = ICompositeLayerState(currLay); + + // Stuff about ZInc + + if (fPipeline->IsDebugFlagSet(plPipeDbg::kFlagDisableSpecular)) + state.fShadeFlags &= ~hsGMatState::kShadeSpecular; + + if (fPipeline->IsDebugFlagSet(plPipeDbg::kFlagNoAlphaBlending)) + state.fBlendFlags &= ~hsGMatState::kBlendMask; + + if ((fPipeline->IsDebugFlagSet(plPipeDbg::kFlagBumpUV) || fPipeline->IsDebugFlagSet(plPipeDbg::kFlagBumpW)) && (state.fMiscFlags & hsGMatState::kMiscBumpChans) ) { + switch (state.fMiscFlags & hsGMatState::kMiscBumpChans) + { + case hsGMatState::kMiscBumpDu: + break; + case hsGMatState::kMiscBumpDv: + if (!(fMaterial->GetLayer(layer-2)->GetBlendFlags() & hsGMatState::kBlendAdd)) + { + state.fBlendFlags &= ~hsGMatState::kBlendMask; + state.fBlendFlags |= hsGMatState::kBlendMADD; + } + break; + case hsGMatState::kMiscBumpDw: + if (!(fMaterial->GetLayer(layer-1)->GetBlendFlags() & hsGMatState::kBlendAdd)) + { + state.fBlendFlags &= ~hsGMatState::kBlendMask; + state.fBlendFlags |= hsGMatState::kBlendMADD; + } + break; + default: + break; + } + } + + uint32_t currNumLayers = ILayersAtOnce(layer); + + if (state.fMiscFlags & (hsGMatState::kMiscBumpDu | hsGMatState::kMiscBumpDw)) { + //ISetBumpMatrices(currLay); + } + + currLay = fPipeline->IPopOverAllLayer(currLay); + currLay = fPipeline->IPopOverBaseLayer(currLay); + + return layer + currNumLayers; +} + + +uint32_t plGLMaterialShaderRef::ILayersAtOnce(uint32_t which) +{ + uint32_t currNumLayers = 1; + + plLayerInterface* lay = fMaterial->GetLayer(which); + + if (fPipeline->IsDebugFlagSet(plPipeDbg::kFlagNoMultitexture)) + return currNumLayers; + + if ((fPipeline->IsDebugFlagSet(plPipeDbg::kFlagBumpUV) || fPipeline->IsDebugFlagSet(plPipeDbg::kFlagBumpW)) && (lay->GetMiscFlags() & hsGMatState::kMiscBumpChans)) { + currNumLayers = 2; + return currNumLayers; + } + + if ((lay->GetBlendFlags() & hsGMatState::kBlendNoColor) || (lay->GetMiscFlags() & hsGMatState::kMiscTroubledLoner)) + return currNumLayers; + + int i; + int maxLayers = 8; + if (which + maxLayers > fMaterial->GetNumLayers()) + maxLayers = fMaterial->GetNumLayers() - which; + + for (i = currNumLayers; i < maxLayers; i++) { + plLayerInterface* lay = fMaterial->GetLayer(which + i); + + // Ignoring max UVW limit + + if ((lay->GetMiscFlags() & hsGMatState::kMiscBindNext) && (i+1 >= maxLayers)) + break; + + if (lay->GetMiscFlags() & hsGMatState::kMiscRestartPassHere) + break; + + if (!(fMaterial->GetLayer(which + i - 1)->GetMiscFlags() & hsGMatState::kMiscBindNext) && !ICanEatLayer(lay)) + break; + + currNumLayers++; + } + + return currNumLayers; +} + + +bool plGLMaterialShaderRef::ICanEatLayer(plLayerInterface* lay) +{ + if (!lay->GetTexture()) + return false; + + if ((lay->GetBlendFlags() & hsGMatState::kBlendNoColor) || + (lay->GetBlendFlags() & hsGMatState::kBlendAddColorTimesAlpha) || + (lay->GetMiscFlags() & hsGMatState::kMiscTroubledLoner)) + return false; + + if ((lay->GetBlendFlags() & hsGMatState::kBlendAlpha) && (lay->GetAmbientColor().a < 1.f)) + return false; + + if (!(lay->GetZFlags() & hsGMatState::kZNoZWrite)) + return false; + + return true; +} diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLMaterialShaderRef.h b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLMaterialShaderRef.h new file mode 100644 index 0000000000..13c1550619 --- /dev/null +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLMaterialShaderRef.h @@ -0,0 +1,151 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef _plGLMaterialShaderRef_inc_ +#define _plGLMaterialShaderRef_inc_ + +#include "plGLDeviceRef.h" + +#include +#include + +#include "hsGMatState.h" + +class hsGMaterial; +class plGLPipeline; +class plLayerInterface; + +enum plGLShaderConstants : GLuint { + kVtxPosition = 0, + kVtxNormal = 1, + kVtxColor = 2, + kVtxUVWSrc = 3 +}; + +class plGLMaterialShaderRef : public plGLDeviceRef +{ + enum { + kShaderVersion = 100 + }; + + struct uniformLightSource { + GLuint position; + GLuint ambient; + GLuint diffuse; + GLuint specular; + GLuint direction; + GLuint spotProps; + GLuint constAtten; + GLuint linAtten; + GLuint quadAtten; + GLuint scale; + }; + +protected: + hsGMaterial* fMaterial; + plGLPipeline* fPipeline; + GLuint fVertShaderRef; + GLuint fFragShaderRef; + + std::vector fPassIndices; + +public: + // These are named to match the GLSL variable names and are public vars + GLuint aVtxPosition; + GLuint aVtxNormal; + GLuint aVtxColor; + std::vector aVtxUVWSrc; // These are indexed by UV chan + std::vector uLayerMat; // These are indexed by layer + std::vector uTexture; // These are indexed by layer + GLuint uMatrixProj; + GLuint uMatrixC2W; + GLuint uMatrixW2C; + GLuint uMatrixL2W; + GLuint uMatrixW2L; + GLuint uGlobalAmbient; + GLuint uMatAmbientCol; + GLuint uMatAmbientSrc; + GLuint uMatDiffuseCol; + GLuint uMatDiffuseSrc; + GLuint uMatEmissiveCol; + GLuint uMatEmissiveSrc; + GLuint uMatSpecularCol; + GLuint uMatSpecularSrc; + GLuint uPassNumber; + GLuint uAlphaThreshold; + GLuint uInvertVtxAlpha; + GLuint uFogExponential; + GLuint uFogValues; + GLuint uFogColor; + uniformLightSource uLampSources[8]; + + void Link(plGLMaterialShaderRef** back) { plGLDeviceRef::Link((plGLDeviceRef**)back); } + plGLMaterialShaderRef* GetNext() { return (plGLMaterialShaderRef*)fNext; } + + plGLMaterialShaderRef(hsGMaterial* mat, plGLPipeline* pipe); + virtual ~plGLMaterialShaderRef(); + + void Release(); + void SetupTextureRefs(); + + size_t GetNumPasses() const { return fPassIndices.size(); } + size_t GetPassIndex(size_t which) const { return fPassIndices[which]; } + hsGMaterial* GetMaterial() const { return fMaterial; } + +protected: + void ICompile(); + + void ISetShaderVariableLocs(); + + void ILoopOverLayers(); + + // Set the current Plasma state based on the input layer state and the material overrides. + // fMatOverOn overrides to set a state bit whether it is set in the layer or not. + // fMatOverOff overrides to clear a state bit whether it is set in the layer or not. + const hsGMatState ICompositeLayerState(plLayerInterface* layer); + + uint32_t IHandleMaterial(uint32_t layer); + uint32_t ILayersAtOnce(uint32_t which); + bool ICanEatLayer(plLayerInterface* lay); +}; + +#endif // _plGLMaterialShaderRef_inc_ diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLPipeline.cpp b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLPipeline.cpp index 4ed7e6b6a6..e8d2deb5c9 100644 --- a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLPipeline.cpp +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLPipeline.cpp @@ -46,51 +46,240 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com // // /////////////////////////////////////////////////////////////////////////////// +#include +#include + #include "HeadSpin.h" +#include "hsSIMD.h" #include "hsWindows.h" +#include "plQuality.h" -#include +#include "plGLMaterialShaderRef.h" +#include "plGLPipeline.h" +#include "plGLPlateManager.h" +#include "plGLTextFont.h" +#include "hsGMatState.inl" +#include "plPipeDebugFlags.h" +#include "plPipeResReq.h" +#include "plProfile.h" +#include "pnNetCommon/plNetApp.h" // for dbg logging +#include "pnMessage/plPipeResMakeMsg.h" +#include "plAvatar/plAvatarClothing.h" +#include "plGImage/plMipmap.h" +#include "plGLight/plLightInfo.h" #include "plPipeline/hsWinRef.h" +#include "plPipeline/plCubicRenderTarget.h" +#include "plPipeline/plDebugText.h" +#include "plPipeline/plDynamicEnvMap.h" +#include "plStatusLog/plStatusLog.h" +#include "plSurface/plLayer.h" -#include "plGLPipeline.h" -#include "plGLPlateManager.h" +typedef plGLDevice DeviceType; + +plProfile_Extern(DrawFeedTriangles); +plProfile_Extern(DrawPrimStatic); +plProfile_Extern(TotalTexSize); +plProfile_Extern(LayChange); +plProfile_Extern(DrawTriangles); +plProfile_Extern(MatChange); +plProfile_Extern(PrepDrawable); +plProfile_Extern(Skin); +plProfile_Extern(RenderSpan); +plProfile_Extern(MergeCheck); +plProfile_Extern(MergeSpan); +plProfile_Extern(SpanTransforms); +plProfile_Extern(SpanFog); +plProfile_Extern(SelectLights); +plProfile_Extern(SelectProj); +plProfile_Extern(CheckDyn); +plProfile_Extern(CheckStat); +plProfile_Extern(RenderBuff); +plProfile_Extern(RenderPrim); +plProfile_Extern(PlateMgr); +plProfile_Extern(DebugText); +plProfile_Extern(Reset); +plProfile_Extern(NumSkin); +plProfile_Extern(PipeReload); +plProfile_Extern(AvRTPoolUsed); +plProfile_Extern(AvRTPoolCount); +plProfile_Extern(AvRTPoolRes); +plProfile_Extern(AvRTShrinkTime); +plProfile_Extern(SpanMerge); +plProfile_Extern(MatLightState); +plProfile_Extern(EmptyList); + +// Adding a nil RenderPrim for turning off drawing +static plRenderNilFunc sRenderNil; + +class plGLRenderTriListFunc : public plRenderTriListFunc +{ +public: + plGLRenderTriListFunc(plGLDevice* device, int baseVertexIndex, int vStart, int vLength, int iStart, int iNumTris) + : plRenderTriListFunc(device, baseVertexIndex, vStart, vLength, iStart, iNumTris) {} + + bool RenderPrims() const override + { + plProfile_IncCount(DrawFeedTriangles, fNumTris); + plProfile_IncCount(DrawTriangles, fNumTris); + plProfile_Inc(DrawPrimStatic); + + glDrawElements(GL_TRIANGLES, fNumTris, GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(uint16_t) * fIStart)); + return true; // TODO: Check for GL Error + } +}; + + +template +static inline void inlCopy(uint8_t*& src, uint8_t*& dst) +{ + T* src_ptr = reinterpret_cast(src); + T* dst_ptr = reinterpret_cast(dst); + *dst_ptr = *src_ptr; + src += sizeof(T); + dst += sizeof(T); +} + +template +static inline const uint8_t* inlExtract(const uint8_t* src, T* val) +{ + const T* ptr = reinterpret_cast(src); + *val = *ptr++; + return reinterpret_cast(ptr); +} + +template<> +inline const uint8_t* inlExtract(const uint8_t* src, hsPoint3* val) +{ + const float* src_ptr = reinterpret_cast(src); + float* dst_ptr = reinterpret_cast(val); + *dst_ptr++ = *src_ptr++; + *dst_ptr++ = *src_ptr++; + *dst_ptr++ = *src_ptr++; + *dst_ptr = 1.f; + return reinterpret_cast(src_ptr); +} + +template<> +inline const uint8_t* inlExtract(const uint8_t* src, hsVector3* val) +{ + const float* src_ptr = reinterpret_cast(src); + float* dst_ptr = reinterpret_cast(val); + *dst_ptr++ = *src_ptr++; + *dst_ptr++ = *src_ptr++; + *dst_ptr++ = *src_ptr++; + *dst_ptr = 0.f; + return reinterpret_cast(src_ptr); +} + +template +static inline void inlSkip(uint8_t*& src) +{ + src += sizeof(T) * N; +} + +template +static inline uint8_t* inlStuff(uint8_t* dst, const T* val) +{ + T* ptr = reinterpret_cast(dst); + *ptr++ = *val; + return reinterpret_cast(ptr); +} -#ifdef HS_SIMD_INCLUDE -# include HS_SIMD_INCLUDE -#endif plGLEnumerate plGLPipeline::enumerator; plGLPipeline::plGLPipeline(hsWindowHndl display, hsWindowHndl window, const hsG3DDeviceModeRecord *devMode) - : pl3DPipeline(devMode) + : pl3DPipeline(devMode), fMatRefList(), fRenderTargetRefList() { + plStatusLog::AddLineS("pipeline.log", "Constructing plGLPipeline"); + plStatusLog::AddLineSF("pipeline.log", "Driver vendor: {}", devMode->GetDevice()->GetDriverDesc()); + + fDevice.Setup(this, window, display); + fPlateMgr = new plGLPlateManager(this); } +plGLPipeline::~plGLPipeline() +{ + if (plGLPlateManager* pm = static_cast(fPlateMgr)) + pm->IReleaseGeometry(); + + delete fPlateMgr; + fPlateMgr = nullptr; + + while (fTextFontRefList) + delete fTextFontRefList; + + delete fDebugTextMgr; + fDebugTextMgr = nullptr; + + fDevice.Shutdown(); +} + bool plGLPipeline::PreRender(plDrawable* drawable, std::vector& visList, plVisMgr* visMgr) { - return false; + plDrawableSpans* ds = plDrawableSpans::ConvertNoRef(drawable); + if (!ds) + return false; + + if ((ds->GetType() & fView.GetDrawableTypeMask()) == 0) + return false; + + fView.GetVisibleSpans(ds, visList, visMgr); + + // TODO: Implement bounds debugging stuff here + + return !visList.empty(); } bool plGLPipeline::PrepForRender(plDrawable* drawable, std::vector& visList, plVisMgr* visMgr) { - return false; -} + plProfile_BeginTiming(PrepDrawable); -void plGLPipeline::Render(plDrawable* d, const std::vector& visList) -{} + plDrawableSpans* ice = plDrawableSpans::ConvertNoRef(drawable); + if (!ice) { + plProfile_EndTiming(PrepDrawable); + return false; + } -plTextFont* plGLPipeline::MakeTextFont(ST::string face, uint16_t size) -{ - return nullptr; + // Find our lights + ICheckLighting(ice, visList, visMgr); + + // Sort our faces + if (ice->GetNativeProperty(plDrawable::kPropSortFaces)) + ice->SortVisibleSpans(visList, this); + + // Prep for render. This is gives the drawable a chance to + // do any last minute updates for its buffers, including + // generating particle tri lists. + ice->PrepForRender(this); + + // Any skinning necessary + if (!ISoftwareVertexBlend(ice, visList)) { + plProfile_EndTiming(PrepDrawable); + return false; + } + + // Avatar face sorting happens after the software skin. + if (ice->GetNativeProperty(plDrawable::kPropPartialSort)) + IAvatarSort(ice, visList); + + plProfile_EndTiming(PrepDrawable); + + return true; } -void plGLPipeline::CheckVertexBufferRef(plGBufferGroup* owner, uint32_t idx) -{} +plTextFont* plGLPipeline::MakeTextFont(ST::string face, uint16_t size) +{ + plTextFont* font = new plGLTextFont(this, nullptr); + if (!font) + return nullptr; -void plGLPipeline::CheckIndexBufferRef(plGBufferGroup* owner, uint32_t idx) -{} + font->Create(face, size); + font->Link(&fTextFontRefList); + return font; +} bool plGLPipeline::OpenAccess(plAccessSpan& dst, plDrawableSpans* d, const plVertexSpan* span, bool readOnly) { @@ -102,84 +291,363 @@ bool plGLPipeline::CloseAccess(plAccessSpan& acc) return false; } -void plGLPipeline::CheckTextureRef(plLayerInterface* lay) -{} - void plGLPipeline::PushRenderRequest(plRenderRequest* req) -{} +{ + // Save these, since we want to copy them to our current view + hsMatrix44 l2w = fView.GetLocalToWorld(); + hsMatrix44 w2l = fView.GetWorldToLocal(); + + plFogEnvironment defFog = fView.GetDefaultFog(); + + fViewStack.push(fView); + + SetViewTransform(req->GetViewTransform()); + + PushRenderTarget(req->GetRenderTarget()); + fView.fRenderState = req->GetRenderState(); + + fView.fRenderRequest = req; + hsRefCnt_SafeRef(fView.fRenderRequest); + + SetDrawableTypeMask(req->GetDrawableMask()); + SetSubDrawableTypeMask(req->GetSubDrawableMask()); + + float depth = req->GetClearDepth(); + fView.SetClear(&req->GetClearColor(), &depth); + + if (req->GetOverrideMat()) + PushOverrideMaterial(req->GetOverrideMat()); + + // Set from our saved ones... + fView.SetWorldToLocal(w2l); + fView.SetLocalToWorld(l2w); + + RefreshMatrices(); + + if (req->GetIgnoreOccluders()) + fView.SetMaxCullNodes(0); + + fView.fCullTreeDirty = true; +} void plGLPipeline::PopRenderRequest(plRenderRequest* req) -{} +{ + if (req->GetOverrideMat()) + PopOverrideMaterial(nullptr); + + hsRefCnt_SafeUnRef(fView.fRenderRequest); + fView = fViewStack.top(); + fViewStack.pop(); + + PopRenderTarget(); + fView.fXformResetFlags = fView.kResetProjection | fView.kResetCamera; +} void plGLPipeline::ClearRenderTarget(plDrawable* d) -{} +{ + plDrawableSpans* src = plDrawableSpans::ConvertNoRef(d); + + ClearRenderTarget(); + + if (!src) + return; +} void plGLPipeline::ClearRenderTarget(const hsColorRGBA* col, const float* depth) -{} +{ + if (fView.fRenderState & (kRenderClearColor | kRenderClearDepth)) { + hsColorRGBA clearColor = col ? *col : GetClearColor(); + float clearDepth = depth ? *depth : fView.GetClearDepth(); + + GLuint masks = 0; + if (fView.fRenderState & kRenderClearColor) + masks |= GL_COLOR_BUFFER_BIT; + if (fView.fRenderState & kRenderClearDepth) + masks |= GL_DEPTH_BUFFER_BIT; + + glDepthMask(GL_TRUE); + glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); + glClearDepth(1.0); + + glClear(masks); + } +} hsGDeviceRef* plGLPipeline::MakeRenderTargetRef(plRenderTarget* owner) { - return nullptr; -} + plGLRenderTargetRef* ref = nullptr; + GLuint depthBuffer = -1; -void plGLPipeline::PushRenderTarget(plRenderTarget* target) -{} + // If we have Shader Model 3 and support non-POT textures, let's make reflections the pipe size + if (plDynamicCamMap* camMap = plDynamicCamMap::ConvertNoRef(owner)) { + if (camMap->IsReflection() && plQuality::GetCapability() > plQuality::kPS_2) + camMap->ResizeViewport(IGetViewTransform()); + } -plRenderTarget* plGLPipeline::PopRenderTarget() -{ - return nullptr; + /// Check--is this renderTarget really a child of a cubicRenderTarget? + if (owner->GetParent()) { + /// This'll create the deviceRefs for all of its children as well + MakeRenderTargetRef(owner->GetParent()); + return owner->GetDeviceRef(); + } + + // If we already have a rendertargetref, we just need it filled out with D3D resources. + if (owner->GetDeviceRef()) + ref = (plGLRenderTargetRef*)owner->GetDeviceRef(); + + /// Create the render target now + // Start with the depth surface. + // Note that we only ever give a cubic rendertarget a single shared depth buffer, + // since we only render one face at a time. If we were rendering part of face X, then part + // of face Y, then more of face X, then they would all need their own depth buffers. + if (owner->GetZDepth() && (owner->GetFlags() & (plRenderTarget::kIsTexture | plRenderTarget::kIsOffscreen))) { + if (plGLVersion() >= 45) { + glCreateRenderbuffers(1, &depthBuffer); + glNamedRenderbufferStorage(depthBuffer, GL_DEPTH24_STENCIL8, owner->GetWidth(), owner->GetHeight()); + } else { + glGenRenderbuffers(1, &depthBuffer); + glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, owner->GetWidth(), owner->GetHeight()); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + } + } + + // See if it's a cubic render target. + // Primary consumer here is the vertex/pixel shader water. + if (plCubicRenderTarget* cubicRT = plCubicRenderTarget::ConvertNoRef(owner)) { +#if 0 + if (!ref) + ref = new plGLRenderTargetRef(); + + ref->fOwner = owner; + ref->fDepthBuffer = depthBuffer; + ref->fMapping = GL_TEXTURE_CUBE_MAP; + + if (plGLVersion() >= 45) { + glCreateTextures(GL_TEXTURE_CUBE_MAP, 1, &ref->fRef); + // TODO: The rest + } else { + glGenTextures(1, &ref->fRef); + glBindTexture(GL_TEXTURE_CUBE_MAP, ref->fRef); + // TODO: The rest + } +#endif + } + + // Not a cubic, is it a texture render target? These are currently used + // primarily for shadow map generation. + else if (owner->GetFlags() & plRenderTarget::kIsTexture) { + /// Create a normal texture + if (!ref) + ref = new plGLRenderTargetRef(); + + ref->fOwner = owner; + ref->fDepthBuffer = depthBuffer; + ref->fMapping = GL_TEXTURE_2D; + + if (plGLVersion() >= 45) { + glCreateTextures(GL_TEXTURE_2D, 1, &ref->fRef); + glTextureStorage2D(ref->fRef, 1, GL_RGBA8, owner->GetWidth(), owner->GetHeight()); + glTextureParameteri(ref->fRef, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTextureParameteri(ref->fRef, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glCreateFramebuffers(1, &ref->fFrameBuffer); + glNamedFramebufferTexture(ref->fFrameBuffer, GL_COLOR_ATTACHMENT0, ref->fRef, 0); + if (ref->fDepthBuffer != -1) + glNamedFramebufferRenderbuffer(ref->fFrameBuffer, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, ref->fDepthBuffer); + } else { + glGenTextures(1, &ref->fRef); + glBindTexture(GL_TEXTURE_2D, ref->fRef); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, owner->GetWidth(), owner->GetHeight()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); + + glGenFramebuffers(1, &ref->fFrameBuffer); + glBindFramebuffer(GL_FRAMEBUFFER, ref->fFrameBuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ref->fRef, 0); + if (ref->fDepthBuffer != -1) + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, ref->fDepthBuffer); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + } + + // Not a texture either, must be a plain offscreen. + // Offscreen isn't currently used for anything. + else if (owner->GetFlags() & plRenderTarget::kIsOffscreen) { + /// Create a blank surface + //if (ref) + // ref->Set(surfFormat, 0, owner); + //else + // ref = new plGLRenderTargetRef(surfFormat, 0, owner); + } + + // Keep it in a linked list for ready destruction. + if (owner->GetDeviceRef() != ref) { + owner->SetDeviceRef(ref); + + // Unref now, since for now ONLY the RT owns the ref, not us (not until we use it, at least) + hsRefCnt_SafeUnRef(ref); + if (ref != nullptr && !ref->IsLinked()) + ref->Link(&fRenderTargetRefList); + } else { + if (ref != nullptr && !ref->IsLinked()) + ref->Link(&fRenderTargetRefList); + } + + // Mark as not dirty so it doesn't get re-created + if (ref != nullptr) + ref->SetDirty(false); + else { + hsStatusMessage("Got an unfilled render target!"); + } + + return ref; } bool plGLPipeline::BeginRender() { + // offset transform + RefreshScreenMatrices(); + + // If this is the primary BeginRender, make sure we're really ready. + if (fInSceneDepth++ == 0) { + fDevice.BeginRender(); + + fVtxRefTime++; + + IPreprocessAvatarTextures(); + + hsColorRGBA clearColor = GetClearColor(); + + glDepthMask(GL_TRUE); + glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); + glClearDepth(1.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + fRenderCnt++; + + // Would probably rather this be an input. + fTime = hsTimer::GetSysSeconds(); + return false; } bool plGLPipeline::EndRender() { - return false; + bool retVal = false; + + // Actually end the scene + if (--fInSceneDepth == 0) { + retVal = fDevice.EndRender(); + + IClearShadowSlaves(); + } + + // Do this last, after we've drawn everything + // Just letting go of things we're done with for the frame. + hsRefCnt_SafeUnRef(fCurrMaterial); + fCurrMaterial = nullptr; + + for (int i = 0; i < 8; i++) { + if (fLayerRef[i]) { + hsRefCnt_SafeUnRef(fLayerRef[i]); + fLayerRef[i] = nullptr; + } + } + + return retVal; } void plGLPipeline::RenderScreenElements() -{} - -bool plGLPipeline::IsFullScreen() const { - return false; + bool reset = false; + + if (fView.HasCullProxy()) + Draw(fView.GetCullProxy()); + + hsGMatState tHack = PushMaterialOverride(hsGMatState::kMisc, hsGMatState::kMiscWireFrame, false); + hsGMatState ambHack = PushMaterialOverride(hsGMatState::kShade, hsGMatState::kShadeWhite, true); + + plProfile_BeginTiming(PlateMgr); + // Plates + if (fPlateMgr) { + fPlateMgr->DrawToDevice(this); + reset = true; + } + plProfile_EndTiming(PlateMgr); + + PopMaterialOverride(ambHack, true); + PopMaterialOverride(tHack, false); + + plProfile_BeginTiming(DebugText); + /// Debug text + if (fDebugTextMgr && plDebugText::Instance().IsEnabled()) { + fDebugTextMgr->DrawToDevice(this); + reset = true; + } + plProfile_EndTiming(DebugText); + + plProfile_BeginTiming(Reset); + if (reset) + fView.fXformResetFlags = fView.kResetAll; // Text destroys view transforms + plProfile_EndTiming(Reset); } -uint32_t plGLPipeline::ColorDepth() const +bool plGLPipeline::IsFullScreen() const { - return 0; + return false; } void plGLPipeline::Resize(uint32_t width, uint32_t height) {} -bool plGLPipeline::CheckResources() +void plGLPipeline::LoadResources() { - return false; -} + hsStatusMessageF("Begin Device Reload t=%f",hsTimer::GetSeconds()); + plNetClientApp::StaticDebugMsg("Begin Device Reload"); -void plGLPipeline::LoadResources() -{} + if (plGLPlateManager* pm = static_cast(fPlateMgr)) + pm->IReleaseGeometry(); -void plGLPipeline::SetZBiasScale(float scale) -{} + IReleaseAvRTPool(); -float plGLPipeline::GetZBiasScale() const -{ - return 0.0f; -} + if (!fDevice.HasContext()) { + // We can't create anything if the OpenGL context isn't initialized + plProfile_IncCount(PipeReload, 1); -void plGLPipeline::SetWorldToCamera(const hsMatrix44& w2c, const hsMatrix44& c2w) -{} + hsStatusMessageF("End Device Reload (but no GL Context) t=%f",hsTimer::GetSeconds()); + plNetClientApp::StaticDebugMsg("End Device Reload (but no GL Context)"); + return; + } -void plGLPipeline::RefreshScreenMatrices() -{} + fDebugTextMgr = new plDebugTextManager(); -void plGLPipeline::SubmitClothingOutfit(plClothingOutfit* co) -{} + // Create all RenderTargets + plPipeRTMakeMsg* rtMake = new plPipeRTMakeMsg(this); + rtMake->Send(); + + if (plGLPlateManager* pm = static_cast(fPlateMgr)) + pm->ICreateGeometry(); + + plPipeGeoMakeMsg* defMake = new plPipeGeoMakeMsg(this, true); + defMake->Send(); + + IFillAvRTPool(); + + // Force a create of all our static vertex buffers. + plPipeGeoMakeMsg* manMake = new plPipeGeoMakeMsg(this, false); + manMake->Send(); + + // Okay, we've done it, clear the request. + plPipeResReq::Clear(); + + plProfile_IncCount(PipeReload, 1); + + hsStatusMessageF("End Device Reload t=%f",hsTimer::GetSeconds()); + plNetClientApp::StaticDebugMsg("End Device Reload"); +} bool plGLPipeline::SetGamma(float eR, float eG, float eB) { @@ -218,4 +686,1361 @@ void plGLPipeline::ResetDisplayDevice(int Width, int Height, int ColorDepth, boo {} void plGLPipeline::RenderSpans(plDrawableSpans* ice, const std::vector& visList) -{} +{ + plProfile_BeginTiming(RenderSpan); + + hsMatrix44 lastL2W; + hsGMaterial* material; + const std::vector& spans = ice->GetSpanArray(); + + plProfile_IncCount(EmptyList, visList.empty()); + + /// Set this (*before* we do our TestVisibleWorld stuff...) + lastL2W.Reset(); + ISetLocalToWorld(lastL2W, lastL2W); // This is necessary; otherwise, we have to test for + // the first transform set, since this'll be identity + // but the actual device transform won't be (unless + // we do this) + + /// Loop through our spans, combining them when possible + for (size_t i = 0; i < visList.size(); ) + { + if (GetOverrideMaterial() != nullptr) + material = GetOverrideMaterial(); + else + material = ice->GetMaterial(spans[visList[i]]->fMaterialIdx); + + /// It's an icicle--do our icicle merge loop + plIcicle tempIce(*((plIcicle*)spans[visList[i]])); + + // Start at i + 1, look for as many spans as we can add to tempIce + size_t j; + for (j = i + 1; j < visList.size(); j++) + { + if (GetOverrideMaterial()) + tempIce.fMaterialIdx = spans[visList[j]]->fMaterialIdx; + + plProfile_BeginTiming(MergeCheck); + if (!spans[visList[j]]->CanMergeInto(&tempIce)) { + plProfile_EndTiming(MergeCheck); + break; + } + plProfile_EndTiming(MergeCheck); + plProfile_Inc(SpanMerge); + + plProfile_BeginTiming(MergeSpan); + spans[visList[j]]->MergeInto(&tempIce); + plProfile_EndTiming(MergeSpan); + } + + LOG_GL_ERROR_CHECK("RenderSpans pre-material failed"); + + if (material != nullptr) { + // First, do we have a device ref at this index? + plGLMaterialShaderRef* mRef = static_cast(material->GetDeviceRef()); + + if (mRef == nullptr) { + mRef = new plGLMaterialShaderRef(material, this); + material->SetDeviceRef(mRef); + hsRefCnt_SafeUnRef(mRef); + } + + if (!mRef->IsLinked()) + mRef->Link(&fMatRefList); + + glUseProgram(mRef->fRef); + fDevice.SetCurrentProgram(mRef->fRef); + LOG_GL_ERROR_CHECK(ST::format("Use Program with material \"{}\" failed", material->GetKeyName())); + + GLuint vao = 0; + if (plGLVersion() >= 30) { + // TODO: Figure out how to use VAOs properly :( + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + } + + // What do we change? + + plProfile_BeginTiming(SpanTransforms); + ISetupTransforms(ice, tempIce, mRef, lastL2W); + plProfile_EndTiming(SpanTransforms); + + // Check that the underlying buffers are ready to go. + plProfile_BeginTiming(CheckDyn); + ICheckDynBuffers(ice, ice->GetBufferGroup(tempIce.fGroupIdx), &tempIce); + plProfile_EndTiming(CheckDyn); + + plProfile_BeginTiming(CheckStat); + plGBufferGroup* grp = ice->GetBufferGroup(tempIce.fGroupIdx); + CheckVertexBufferRef(grp, tempIce.fVBufferIdx); + CheckIndexBufferRef(grp, tempIce.fIBufferIdx); + plProfile_EndTiming(CheckStat); + + // Draw this span now + IRenderBufferSpan( tempIce, + ice->GetVertexRef( tempIce.fGroupIdx, tempIce.fVBufferIdx ), + ice->GetIndexRef( tempIce.fGroupIdx, tempIce.fIBufferIdx ), + material, + tempIce.fVStartIdx, tempIce.fVLength, // These are used as our accumulated range + tempIce.fIPackedIdx, tempIce.fILength ); + + if (plGLVersion() >= 30) + glDeleteVertexArrays(1, &vao); + } + + // Restart our search... + i = j; + } + + plProfile_EndTiming(RenderSpan); + /// All done! +} + + +void plGLPipeline::ISetupTransforms(plDrawableSpans* drawable, const plSpan& span, plGLMaterialShaderRef* mRef, hsMatrix44& lastL2W) +{ + if (span.fNumMatrices) { + if (span.fNumMatrices <= 2) { + ISetLocalToWorld(span.fLocalToWorld, span.fWorldToLocal); + lastL2W = span.fLocalToWorld; + } else { + lastL2W.Reset(); + ISetLocalToWorld(lastL2W, lastL2W); + fView.fLocalToWorldLeftHanded = span.fLocalToWorld.GetParity(); + } + } else if (lastL2W != span.fLocalToWorld) { + ISetLocalToWorld(span.fLocalToWorld, span.fWorldToLocal); + lastL2W = span.fLocalToWorld; + } else { + fView.fLocalToWorldLeftHanded = lastL2W.GetParity(); + } + +#if 0 + if (span.fNumMatrices == 2) { + D3DXMATRIX mat; + IMatrix44ToD3DMatrix(mat, drawable->GetPaletteMatrix(span.fBaseMatrix+1)); + fD3DDevice->SetTransform(D3DTS_WORLDMATRIX(1), &mat); + fD3DDevice->SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_1WEIGHTS); + } else { + fD3DDevice->SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_DISABLE); + } +#endif + + if (mRef) { + /* Push the matrices into the GLSL shader now */ + glUniformMatrix4fv(mRef->uMatrixProj, 1, GL_TRUE, fDevice.GetProjectionMatrix()); + glUniformMatrix4fv(mRef->uMatrixW2C, 1, GL_TRUE, fDevice.GetW2CMatrix()); + glUniformMatrix4fv(mRef->uMatrixL2W, 1, GL_TRUE, fDevice.GetL2WMatrix()); + glUniformMatrix4fv(mRef->uMatrixW2L, 1, GL_TRUE, fDevice.GetW2LMatrix()); + + if (mRef->uMatrixC2W != -1) + glUniformMatrix4fv(mRef->uMatrixC2W, 1, GL_TRUE, fDevice.GetC2WMatrix()); + } +} + +void plGLPipeline::IRenderBufferSpan(const plIcicle& span, + hsGDeviceRef* vb, hsGDeviceRef* ib, + hsGMaterial* material, + uint32_t vStart, uint32_t vLength, + uint32_t iStart, uint32_t iLength) +{ + plProfile_BeginTiming(RenderBuff); + + plGLVertexBufferRef* vRef = static_cast(vb); + plGLIndexBufferRef* iRef = static_cast(ib); + plGLMaterialShaderRef* mRef = static_cast(material->GetDeviceRef()); + + if (!vRef->fRef || !iRef->fRef) { + plProfile_EndTiming(RenderBuff); + + hsAssert(false, "Trying to render a nil buffer pair!"); + return; + } + + LOG_GL_ERROR_CHECK("PRE Render failed"); + + hsRefCnt_SafeAssign(fCurrMaterial, material); + mRef->SetupTextureRefs(); + + /* Vertex Buffer stuff */ + glBindBuffer(GL_ARRAY_BUFFER, vRef->fRef); + + glEnableVertexAttribArray(kVtxPosition); + glVertexAttribPointer(kVtxPosition, 3, GL_FLOAT, GL_FALSE, vRef->fVertexSize, 0); + + size_t weight_offset = 0; + switch (vRef->fFormat & plGBufferGroup::kSkinWeightMask) + { + case plGBufferGroup::kSkinNoWeights: + break; + case plGBufferGroup::kSkin1Weight: + weight_offset += sizeof(float); + break; + case plGBufferGroup::kSkin2Weights: + weight_offset += sizeof(float) * 2; + break; + case plGBufferGroup::kSkin3Weights: + weight_offset += sizeof(float) * 3; + break; + default: + hsAssert( false, "Bad skin weight value in GBufferGroup" ); + } + + if (vRef->fFormat & plGBufferGroup::kSkinIndices) { + weight_offset += sizeof(uint32_t); + } + + glEnableVertexAttribArray(kVtxNormal); + glVertexAttribPointer(kVtxNormal, 3, GL_FLOAT, GL_FALSE, vRef->fVertexSize, (void*)((sizeof(float) * 3) + weight_offset)); + + glEnableVertexAttribArray(kVtxColor); + glVertexAttribPointer(kVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, vRef->fVertexSize, (void*)((sizeof(float) * 3 * 2) + weight_offset)); + + int numUVs = vRef->fOwner->GetNumUVs(); + for (int i = 0; i < numUVs; i++) { + glEnableVertexAttribArray(kVtxUVWSrc + i); + glVertexAttribPointer(kVtxUVWSrc + i, 3, GL_FLOAT, GL_FALSE, vRef->fVertexSize, (void*)((sizeof(float) * 3 * 2) + (sizeof(uint32_t) * 2) + (sizeof(float) * 3 * i) + weight_offset)); + } + + LOG_GL_ERROR_CHECK("Vertex Attributes failed") + + /* Index Buffer stuff and drawing */ + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iRef->fRef); + + plGLRenderTriListFunc render(&fDevice, 0, vStart, vLength, iStart, iLength); + + plProfile_EndTiming(RenderBuff); + + // Turn on this spans lights and turn off the rest. + ISelectLights(&span, mRef); + + for (size_t pass = 0; pass < mRef->GetNumPasses(); pass++) { + // Set uniform to pass + if (mRef->uPassNumber != -1) + glUniform1i(mRef->uPassNumber, pass); + + fCurrLayerIdx = mRef->GetPassIndex(pass); + plLayerInterface* lay = material->GetLayer(fCurrLayerIdx); + + ICalcLighting(mRef, lay, &span); + + hsGMatState s; + s.Composite(lay->GetState(), fMatOverOn, fMatOverOff); + + // If the layer opacity is 0, don't draw it. This prevents it from + // contributing to the Z buffer. This can happen with some models like + // the fire marbles in the neighborhood that have some models for + // physics only, and then can block other rendering in the Z buffer. + // DX pipeline does this in ILoopOverLayers. + if ((s.fBlendFlags & hsGMatState::kBlendAlpha) && lay->GetOpacity() <= 0 && fCurrLightingMethod != plSpan::kLiteVtxPreshaded) + continue; + + IHandleZMode(s); + IHandleBlendMode(s); + + if (s.fBlendFlags & hsGMatState::kBlendInvertVtxAlpha) + glUniform1f(mRef->uInvertVtxAlpha, 1.f); + else + glUniform1f(mRef->uInvertVtxAlpha, 0.f); + + if (s.fBlendFlags & (hsGMatState::kBlendTest | hsGMatState::kBlendAlpha | hsGMatState::kBlendAddColorTimesAlpha) && + !(s.fBlendFlags & hsGMatState::kBlendAlphaAlways)) + { + // AlphaTestHigh is used for reducing sort artifacts on textures that + // are mostly opaque or transparent, but have regions of translucency + // in transition. Like a texture for a bush billboard. It lets there be + // some transparency falloff, but quit drawing before it gets so + // transparent that draw order problems (halos) become apparent. + if (s.fBlendFlags & hsGMatState::kBlendAlphaTestHigh) + glUniform1f(mRef->uAlphaThreshold, 64.f/255.f); + else + glUniform1f(mRef->uAlphaThreshold, 0.00000000001f); + } else { + glUniform1f(mRef->uAlphaThreshold, 0.f); + } + + if (s.fMiscFlags & hsGMatState::kMiscTwoSided) { + glDisable(GL_CULL_FACE); + } else { + glEnable(GL_CULL_FACE); + ISetCullMode(); + } + + // TEMP + render.RenderPrims(); + } + + LOG_GL_ERROR_CHECK("Render failed") +} + +bool plGLPipeline::ICheckDynBuffers(plDrawableSpans* drawable, plGBufferGroup* group, const plSpan* spanBase) +{ + if (!(spanBase->fTypeMask & plSpan::kVertexSpan)) + return false; + + // If we arent' an trilist, we're toast. + if (!(spanBase->fTypeMask & plSpan::kIcicleSpan)) + return false; + + plIcicle* span = (plIcicle*)spanBase; + + DeviceType::VertexBufferRef* vRef = (DeviceType::VertexBufferRef*)group->GetVertexBufferRef(span->fVBufferIdx); + if (!vRef) + return true; + + DeviceType::IndexBufferRef* iRef = (DeviceType::IndexBufferRef*)group->GetIndexBufferRef(span->fIBufferIdx); + if (!iRef) + return true; + + // If our vertex buffer ref is volatile and the timestamp is off + // then it needs to be refilled + if (vRef->Expired(fVtxRefTime)) + IRefreshDynVertices(group, vRef); + + if (iRef->IsDirty()) { + fDevice.FillIndexBufferRef(iRef, group, span->fIBufferIdx); + iRef->SetRebuiltSinceUsed(true); + } + + return false; // No error +} + +bool plGLPipeline::IRefreshDynVertices(plGBufferGroup* group, plGLVertexBufferRef* vRef) +{ + ptrdiff_t size = (group->GetVertBufferEnd(vRef->fIndex) - group->GetVertBufferStart(vRef->fIndex)) * vRef->fVertexSize; + if (!size) + return false; // No error, just nothing to do. + + hsAssert(size > 0, "Bad start and end counts in a group"); + + if (!vRef->fRef) + glGenBuffers(1, &vRef->fRef); + + glBindBuffer(GL_ARRAY_BUFFER, vRef->fRef); + + uint8_t* vData; + if (vRef->fData) + vData = vRef->fData; + else + vData = group->GetVertBufferData(vRef->fIndex) + group->GetVertBufferStart(vRef->fIndex) * vRef->fVertexSize; + + glBufferData(GL_ARRAY_BUFFER, size, vData, GL_DYNAMIC_DRAW); + + vRef->fRefTime = fVtxRefTime; + vRef->SetDirty(false); + + return false; +} + +void plGLPipeline::IHandleZMode(hsGMatState flags) +{ + switch (flags.fZFlags & hsGMatState::kZMask) + { + case hsGMatState::kZClearZ: + glDepthFunc(GL_ALWAYS); + glDepthMask(GL_TRUE); + break; + case hsGMatState::kZNoZRead: + glDepthFunc(GL_ALWAYS); + glDepthMask(GL_TRUE); + break; + case hsGMatState::kZNoZWrite: + glDepthFunc(GL_LEQUAL); + glDepthMask(GL_FALSE); + break; + case hsGMatState::kZNoZRead | hsGMatState::kZClearZ: + glDepthFunc(GL_ALWAYS); + glDepthMask(GL_TRUE); + break; + case hsGMatState::kZNoZRead | hsGMatState::kZNoZWrite: + glDepthFunc(GL_ALWAYS); + glDepthMask(GL_FALSE); + break; + case 0: + glDepthFunc(GL_LEQUAL); + glDepthMask(GL_TRUE); + break; + case hsGMatState::kZClearZ | hsGMatState::kZNoZWrite: + case hsGMatState::kZClearZ | hsGMatState::kZNoZWrite | hsGMatState::kZNoZRead: + hsAssert(false, "Illegal combination of Z Buffer modes (Clear but don't write)"); + break; + } + + if (flags.fZFlags & hsGMatState::kZIncLayer) { + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-1.f, -1.f); + } else { + glPolygonOffset(0.f, 0.f); + glDisable(GL_POLYGON_OFFSET_FILL); + } +} + +void plGLPipeline::IHandleBlendMode(hsGMatState flags) +{ + // No color, just writing out Z values. + if (flags.fBlendFlags & hsGMatState::kBlendNoColor) { + glBlendFunc(GL_ZERO, GL_ONE); + flags.fBlendFlags |= 0x80000000; + } else { + switch (flags.fBlendFlags & hsGMatState::kBlendMask) + { + // Detail is just a special case of alpha, handled in construction of the texture + // mip chain by making higher levels of the chain more transparent. + case hsGMatState::kBlendDetail: + case hsGMatState::kBlendAlpha: + if (flags.fBlendFlags & hsGMatState::kBlendInvertFinalAlpha) { + if (flags.fBlendFlags & hsGMatState::kBlendAlphaPremultiplied) { + glBlendFunc(GL_ONE, GL_SRC_ALPHA); + } else { + glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); + } + } else { + if (flags.fBlendFlags & hsGMatState::kBlendAlphaPremultiplied) { + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } else { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + } + break; + + // Multiply the final color onto the frame buffer. + case hsGMatState::kBlendMult: + if (flags.fBlendFlags & hsGMatState::kBlendInvertFinalColor) { + glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + } else { + glBlendFunc(GL_ZERO, GL_SRC_COLOR); + } + break; + + // Add final color to FB. + case hsGMatState::kBlendAdd: + glBlendFunc(GL_ONE, GL_ONE); + break; + + // Multiply final color by FB color and add it into the FB. + case hsGMatState::kBlendMADD: + glBlendFunc(GL_DST_COLOR, GL_ONE); + break; + + // Final color times final alpha, added into the FB. + case hsGMatState::kBlendAddColorTimesAlpha: + if (flags.fBlendFlags & hsGMatState::kBlendInvertFinalAlpha) { + glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_ONE); + } else { + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + } + break; + + // Overwrite final color onto FB + case 0: + glBlendFunc(GL_ONE, GL_ZERO); + break; + + default: + { + hsAssert(false, ST::format("Too many blend modes specified in material {}", fCurrMaterial->GetKeyName()).c_str()); + + plLayer* lay = plLayer::ConvertNoRef(fCurrMaterial->GetLayer(fCurrLayerIdx)->BottomOfStack()); + if (lay) { + if (lay->GetBlendFlags() & hsGMatState::kBlendAlpha) { + lay->SetBlendFlags((lay->GetBlendFlags() & ~hsGMatState::kBlendMask) | hsGMatState::kBlendAlpha); + } else { + lay->SetBlendFlags((lay->GetBlendFlags() & ~hsGMatState::kBlendMask) | hsGMatState::kBlendAdd); + } + } + } + break; + } + } +} + + +void plGLPipeline::ISetCullMode() +{ + if (fView.IsViewLeftHanded()) + glCullFace(GL_BACK); + else + glCullFace(GL_FRONT); +} + + +void plGLPipeline::ICalcLighting(plGLMaterialShaderRef* mRef, const plLayerInterface* currLayer, const plSpan* currSpan) +{ + plProfile_Inc(MatLightState); + + GLint e; + + if (IsDebugFlagSet(plPipeDbg::kFlagAllBright)) { + glUniform4f(mRef->uGlobalAmbient, 1.0, 1.0, 1.0, 1.0); + + glUniform4f(mRef->uMatAmbientCol, 1.0, 1.0, 1.0, 1.0); + glUniform4f(mRef->uMatDiffuseCol, 1.0, 1.0, 1.0, 1.0); + glUniform4f(mRef->uMatEmissiveCol, 1.0, 1.0, 1.0, 1.0); + glUniform4f(mRef->uMatSpecularCol, 1.0, 1.0, 1.0, 1.0); + + glUniform1f(mRef->uMatAmbientSrc, 1.0); + glUniform1f(mRef->uMatDiffuseSrc, 1.0); + glUniform1f(mRef->uMatEmissiveSrc, 1.0); + glUniform1f(mRef->uMatSpecularSrc, 1.0); + + return; + } + + hsGMatState state; + state.Composite(currLayer->GetState(), fMatOverOn, fMatOverOff); + + uint32_t mode = (currSpan != nullptr) ? (currSpan->fProps & plSpan::kLiteMask) : plSpan::kLiteMaterial; + + if (state.fMiscFlags & hsGMatState::kMiscBumpChans) { + mode = plSpan::kLiteMaterial; + state.fShadeFlags |= hsGMatState::kShadeNoShade | hsGMatState::kShadeWhite; + } + + /// Select one of our three lighting methods + switch (mode) { + case plSpan::kLiteMaterial: // Material shading + { + if (state.fShadeFlags & hsGMatState::kShadeWhite) { + glUniform4f(mRef->uGlobalAmbient, 1.0, 1.0, 1.0, 1.0); + glUniform4f(mRef->uMatAmbientCol, 1.0, 1.0, 1.0, 1.0); + } else if (IsDebugFlagSet(plPipeDbg::kFlagNoPreShade)) { + glUniform4f(mRef->uGlobalAmbient, 0.0, 0.0, 0.0, 1.0); + glUniform4f(mRef->uMatAmbientCol, 0.0, 0.0, 0.0, 1.0); + } else { + hsColorRGBA amb = currLayer->GetPreshadeColor(); + glUniform4f(mRef->uGlobalAmbient, amb.r, amb.g, amb.b, 1.0); + glUniform4f(mRef->uMatAmbientCol, amb.r, amb.g, amb.b, 1.0); + } + + hsColorRGBA dif = currLayer->GetRuntimeColor(); + glUniform4f(mRef->uMatDiffuseCol, dif.r, dif.g, dif.b, currLayer->GetOpacity()); + + hsColorRGBA em = currLayer->GetAmbientColor(); + glUniform4f(mRef->uMatEmissiveCol, em.r, em.g, em.b, 1.0); + + // Set specular properties + if (state.fShadeFlags & hsGMatState::kShadeSpecular) { + hsColorRGBA spec = currLayer->GetSpecularColor(); + glUniform4f(mRef->uMatSpecularCol, spec.r, spec.g, spec.b, 1.0); +#if 0 + mat.Power = currLayer->GetSpecularPower(); +#endif + } else { + glUniform4f(mRef->uMatSpecularCol, 0.0, 0.0, 0.0, 0.0); + } + + glUniform1f(mRef->uMatDiffuseSrc, 1.0); + glUniform1f(mRef->uMatEmissiveSrc, 1.0); + glUniform1f(mRef->uMatSpecularSrc, 1.0); + + if (state.fShadeFlags & hsGMatState::kShadeNoShade) { + glUniform1f(mRef->uMatAmbientSrc, 1.0); + } else { + glUniform1f(mRef->uMatAmbientSrc, 0.0); + } + + fCurrLightingMethod = plSpan::kLiteMaterial; + break; + } + + case plSpan::kLiteVtxPreshaded: // Vtx preshaded + { + glUniform4f(mRef->uGlobalAmbient, 0.0, 0.0, 0.0, 0.0); + glUniform4f(mRef->uMatAmbientCol, 0.0, 0.0, 0.0, 0.0); + glUniform4f(mRef->uMatDiffuseCol, 0.0, 0.0, 0.0, 0.0); + glUniform4f(mRef->uMatEmissiveCol, 0.0, 0.0, 0.0, 0.0); + glUniform4f(mRef->uMatSpecularCol, 0.0, 0.0, 0.0, 0.0); + + glUniform1f(mRef->uMatDiffuseSrc, 0.0); + glUniform1f(mRef->uMatAmbientSrc, 1.0); + glUniform1f(mRef->uMatSpecularSrc, 1.0); + + if (state.fShadeFlags & hsGMatState::kShadeEmissive) + glUniform1f(mRef->uMatEmissiveSrc, 0.0); + else + glUniform1f(mRef->uMatEmissiveSrc, 1.0); + + fCurrLightingMethod = plSpan::kLiteVtxPreshaded; + break; + } + + case plSpan::kLiteVtxNonPreshaded: // Vtx non-preshaded + { + glUniform4f(mRef->uMatAmbientCol, 0.0, 0.0, 0.0, 0.0); + glUniform4f(mRef->uMatDiffuseCol, 0.0, 0.0, 0.0, 0.0); + + hsColorRGBA em = currLayer->GetAmbientColor(); + glUniform4f(mRef->uMatEmissiveCol, em.r, em.g, em.b, 1.0); + + // Set specular properties + if (state.fShadeFlags & hsGMatState::kShadeSpecular) { + hsColorRGBA spec = currLayer->GetSpecularColor(); + glUniform4f(mRef->uMatSpecularCol, spec.r, spec.g, spec.b, 1.0); +#if 0 + mat.Power = currLayer->GetSpecularPower(); +#endif + } else { + glUniform4f(mRef->uMatSpecularCol, 0.0, 0.0, 0.0, 0.0); + } + + hsColorRGBA amb = currLayer->GetPreshadeColor(); + glUniform4f(mRef->uGlobalAmbient, amb.r, amb.g, amb.b, amb.a); + + glUniform1f(mRef->uMatAmbientSrc, 0.0); + glUniform1f(mRef->uMatDiffuseSrc, 0.0); + glUniform1f(mRef->uMatEmissiveSrc, 1.0); + glUniform1f(mRef->uMatSpecularSrc, 1.0); + + fCurrLightingMethod = plSpan::kLiteVtxNonPreshaded; + break; + } + } + + // Piggy-back some temporary fog stuff on the lighting... + const plFogEnvironment* fog = (currSpan ? (currSpan->fFogEnvironment ? currSpan->fFogEnvironment : &fView.GetDefaultFog()) : nullptr); + uint8_t type = fog ? fog->GetType() : plFogEnvironment::kNoFog; + hsColorRGBA color; + + switch (type) { + case plFogEnvironment::kLinearFog: + { + float start, end; + fog->GetPipelineParams(&start, &end, &color); + + if (mRef->uFogExponential != -1) + glUniform1i(mRef->uFogExponential, 0); + if (mRef->uFogValues != -1) + glUniform2f(mRef->uFogValues, start, end); + if (mRef->uFogColor != -1) + glUniform3f(mRef->uFogColor, color.r, color.g, color.b); + break; + } + case plFogEnvironment::kExpFog: + case plFogEnvironment::kExp2Fog: + { + float density; + float power = (type == plFogEnvironment::kExp2Fog) ? 2.0f : 1.0f; + fog->GetPipelineParams(&density, &color); + + if (mRef->uFogExponential != -1) + glUniform1i(mRef->uFogExponential, 1); + if (mRef->uFogValues != -1) + glUniform2f(mRef->uFogValues, power, density); + if (mRef->uFogColor != -1) + glUniform3f(mRef->uFogColor, color.r, color.g, color.b); + break; + } + default: + if (mRef->uFogExponential != -1) + glUniform1i(mRef->uFogExponential, 0); + if (mRef->uFogValues != -1) + glUniform2f(mRef->uFogValues, 0.0, 0.0); + if (mRef->uFogColor != -1) + glUniform3f(mRef->uFogColor, 0.0, 0.0, 0.0); + break; + } +} + +void plGLPipeline::ISelectLights(const plSpan* span, plGLMaterialShaderRef* mRef, bool proj) +{ + const size_t numLights = 8; + size_t i = 0; + int32_t startScale; + float threshhold; + float overHold = 0.3; + float scale; + + if (!IsDebugFlagSet(plPipeDbg::kFlagNoRuntimeLights) && + !(IsDebugFlagSet(plPipeDbg::kFlagNoApplyProjLights) && proj) && + !(IsDebugFlagSet(plPipeDbg::kFlagOnlyApplyProjLights) && !proj)) + { + std::vector& spanLights = span->GetLightList(proj); + + for (i = 0; i < spanLights.size() && i < numLights; i++) { + IEnableLight(mRef, i, spanLights[i]); + } + startScale = i; + + /// Attempt #2: Take some of the n strongest lights (below a given threshhold) and + /// fade them out to nothing as they get closer to the bottom. This way, they fade + /// out of existence instead of pop out. + + if (i < spanLights.size() - 1 && i > 0) { + threshhold = span->GetLightStrength(i, proj); + i--; + overHold = threshhold * 1.5f; + + if (overHold > span->GetLightStrength(0, proj)) { + overHold = span->GetLightStrength(0, proj); + } + + for (; i > 0 && span->GetLightStrength(i, proj) < overHold; i--) { + scale = (overHold - span->GetLightStrength(i, proj)) / (overHold - threshhold); + + IScaleLight(mRef, i, (1 - scale) * span->GetLightScale(i, proj)); + } + startScale = i + 1; + } + + + /// Make sure those lights that aren't scaled....aren't + for (i = 0; i < startScale; i++) { + IScaleLight(mRef, i, span->GetLightScale(i, proj)); + } + } + + for (; i < numLights; i++) { + IDisableLight(mRef, i); + } +} + +void plGLPipeline::IEnableLight(plGLMaterialShaderRef* mRef, size_t i, plLightInfo* light) +{ + hsColorRGBA amb = light->GetAmbient(); + glUniform4f(mRef->uLampSources[i].ambient, amb.r, amb.g, amb.b, amb.a); + + hsColorRGBA diff = light->GetDiffuse(); + glUniform4f(mRef->uLampSources[i].diffuse, diff.r, diff.g, diff.b, diff.a); + + hsColorRGBA spec = light->GetSpecular(); + glUniform4f(mRef->uLampSources[i].specular, spec.r, spec.g, spec.b, spec.a); + + plDirectionalLightInfo* dirLight = nullptr; + plOmniLightInfo* omniLight = nullptr; + plSpotLightInfo* spotLight = nullptr; + + if ((dirLight = plDirectionalLightInfo::ConvertNoRef(light)) != nullptr) + { + hsVector3 lightDir = dirLight->GetWorldDirection(); + glUniform4f(mRef->uLampSources[i].position, lightDir.fX, lightDir.fY, lightDir.fZ, 0.0); + glUniform3f(mRef->uLampSources[i].direction, lightDir.fX, lightDir.fY, lightDir.fZ); + + glUniform1f(mRef->uLampSources[i].constAtten, 1.0f); + glUniform1f(mRef->uLampSources[i].linAtten, 0.0f); + glUniform1f(mRef->uLampSources[i].quadAtten, 0.0f); + } + else if ((omniLight = plOmniLightInfo::ConvertNoRef(light)) != nullptr) + { + hsPoint3 pos = omniLight->GetWorldPosition(); + glUniform4f(mRef->uLampSources[i].position, pos.fX, pos.fY, pos.fZ, 1.0); + + // TODO: Maximum Range + + glUniform1f(mRef->uLampSources[i].constAtten, omniLight->GetConstantAttenuation()); + glUniform1f(mRef->uLampSources[i].linAtten, omniLight->GetLinearAttenuation()); + glUniform1f(mRef->uLampSources[i].quadAtten, omniLight->GetQuadraticAttenuation()); + + if (!omniLight->GetProjection() && (spotLight = plSpotLightInfo::ConvertNoRef(omniLight)) != nullptr) { + hsVector3 lightDir = spotLight->GetWorldDirection(); + glUniform3f(mRef->uLampSources[i].direction, lightDir.fX, lightDir.fY, lightDir.fZ); + + float falloff = spotLight->GetFalloff(); + float theta = cosf(spotLight->GetSpotInner()); + float phi = cosf(spotLight->GetProjection() ? hsConstants::half_pi : spotLight->GetSpotOuter()); + + glUniform3f(mRef->uLampSources[i].spotProps, falloff, theta, phi); + } else { + glUniform3f(mRef->uLampSources[i].spotProps, 0.0, 0.0, 0.0); + } + } + else { + IDisableLight(mRef, i); + } +} + +void plGLPipeline::IDisableLight(plGLMaterialShaderRef* mRef, size_t i) +{ + glUniform4f(mRef->uLampSources[i].position, 0.0f, 0.0f, 0.0f, 0.0f); + glUniform4f(mRef->uLampSources[i].ambient, 0.0f, 0.0f, 0.0f, 0.0f); + glUniform4f(mRef->uLampSources[i].diffuse, 0.0f, 0.0f, 0.0f, 0.0f); + glUniform4f(mRef->uLampSources[i].specular, 0.0f, 0.0f, 0.0f, 0.0f); + glUniform1f(mRef->uLampSources[i].constAtten, 1.0f); + glUniform1f(mRef->uLampSources[i].linAtten, 0.0f); + glUniform1f(mRef->uLampSources[i].quadAtten, 0.0f); + glUniform1f(mRef->uLampSources[i].scale, 0.0f); +} + +void plGLPipeline::IScaleLight(plGLMaterialShaderRef* mRef, size_t i, float scale) +{ + scale = int(scale * 1.e1f) * 1.e-1f; + glUniform1f(mRef->uLampSources[i].scale, scale); +} + +void plGLPipeline::IDrawPlate(plPlate* plate) +{ + hsGMaterial* material = plate->GetMaterial(); + + // To override the transform done by the z-bias + static float projMat[16] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 2.0f, -2.0f, + 0.0f, 0.0f, 1.0f, 0.0f + }; + + /// Set up the transform directly + fDevice.SetLocalToWorldMatrix(plate->GetTransform()); + + //IPushPiggyBacks(material); + + // First, do we have a device ref at this index? + plGLMaterialShaderRef* mRef = (plGLMaterialShaderRef*)material->GetDeviceRef(); + + if (mRef == nullptr) { + mRef = new plGLMaterialShaderRef(material, this); + material->SetDeviceRef(mRef); + hsRefCnt_SafeUnRef(mRef); + } + + if (!mRef->IsLinked()) + mRef->Link(&fMatRefList); + + glUseProgram(mRef->fRef); + fDevice.SetCurrentProgram(mRef->fRef); + + mRef->SetupTextureRefs(); + + plLayerInterface* lay = material->GetLayer(0); + hsGMatState s; + s.Composite(lay->GetState(), fMatOverOn, fMatOverOff); + + IHandleZMode(s); + IHandleBlendMode(s); + + /* Push the matrices into the GLSL shader now */ + glUniformMatrix4fv(mRef->uMatrixProj, 1, GL_TRUE, projMat); + glUniformMatrix4fv(mRef->uMatrixW2C, 1, GL_TRUE, fDevice.GetW2CMatrix()); + glUniformMatrix4fv(mRef->uMatrixC2W, 1, GL_TRUE, fDevice.GetC2WMatrix()); + glUniformMatrix4fv(mRef->uMatrixL2W, 1, GL_TRUE, fDevice.GetL2WMatrix()); + + if (mRef->uMatrixW2L != -1) + glUniformMatrix4fv(mRef->uMatrixW2L, 1, GL_TRUE, fDevice.GetW2LMatrix()); + + glUniform1f(mRef->uInvertVtxAlpha, 0.f); + glUniform1f(mRef->uAlphaThreshold, 0.f); + + glUniform4f(mRef->uGlobalAmbient, 1.0, 1.0, 1.0, 1.0); + + glUniform4f(mRef->uMatAmbientCol, 1.0, 1.0, 1.0, 1.0); + glUniform4f(mRef->uMatDiffuseCol, 1.0, 1.0, 1.0, lay->GetOpacity()); + glUniform4f(mRef->uMatEmissiveCol, 1.0, 1.0, 1.0, 1.0); + glUniform4f(mRef->uMatSpecularCol, 1.0, 1.0, 1.0, 1.0); + + glUniform1f(mRef->uMatAmbientSrc, 1.0); + glUniform1f(mRef->uMatDiffuseSrc, 1.0); + glUniform1f(mRef->uMatEmissiveSrc, 1.0); + glUniform1f(mRef->uMatSpecularSrc, 1.0); + + glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_SHORT, (GLvoid*)(sizeof(uint16_t) * 0)); +} + +struct plAVTexVert +{ + float fPos[2]; + float fUv[2]; +}; + +static const char* AVATAR_VERTEX_SHADER_STRING = R"(#version 430 + +layout(location = 0) in vec2 aVtxPosition; +layout(location = 1) in vec2 aVtxUV; + +out vec2 vVtxUV; + +void main() { + vVtxUV = aVtxUV; + gl_Position = vec4(aVtxPosition, 0.0, 1.0); +})"; + +static const char* AVATAR_FRAGMENT_SHADER_STRING = R"(#version 430 +precision mediump float; + +layout(location = 0) uniform sampler2D uTex; +layout(location = 1) uniform vec4 uColor; + +in highp vec2 vVtxUV; +out vec4 fragColor; + +void main() { + fragColor = texture(uTex, vVtxUV.xy) * uColor; +})"; + +void plGLPipeline::IPreprocessAvatarTextures() +{ + static GLuint sVertShader = 0; + static GLuint sFragShader = 0; + static GLuint sProgram = 0; + + plProfile_Set(AvRTPoolUsed, fClothingOutfits.size()); + plProfile_Set(AvRTPoolCount, fAvRTPool.size()); + plProfile_Set(AvRTPoolRes, fAvRTWidth); + plProfile_Set(AvRTShrinkTime, uint32_t(hsTimer::GetSysSeconds() - fAvRTShrinkValidSince)); + + // Frees anyone used last frame that we don't need this frame + IClearClothingOutfits(&fPrevClothingOutfits); + + if (fClothingOutfits.empty()) + return; + + // Set up the shaders the first time to go through here + if (!sVertShader) { + GLuint vshader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vshader, 1, &AVATAR_VERTEX_SHADER_STRING, nullptr); + glCompileShader(vshader); + LOG_GL_ERROR_CHECK("Vertex Shader compile failed"); + + sVertShader = vshader; + } + + if (!sFragShader) { + GLuint fshader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fshader, 1, &AVATAR_FRAGMENT_SHADER_STRING, nullptr); + glCompileShader(fshader); + LOG_GL_ERROR_CHECK("Vertex Shader compile failed"); + + sFragShader = fshader; + } + + if (!sProgram) { + GLuint program = glCreateProgram(); + LOG_GL_ERROR_CHECK("Create Program failed"); + + if (plGLVersion() >= 43) { + const char* name = "AvatarClothing"; + glObjectLabel(GL_PROGRAM, program, strlen(name), name); + } + + glAttachShader(program, sVertShader); + LOG_GL_ERROR_CHECK("Attach Vertex Shader failed"); + + glAttachShader(program, sFragShader); + LOG_GL_ERROR_CHECK("Attach Fragment Shader failed"); + + glLinkProgram(program); + LOG_GL_ERROR_CHECK("Program Link failed"); + + GLint isLinked = 0; + glGetProgramiv(program, GL_LINK_STATUS, &isLinked); + if (isLinked == GL_FALSE) + { + GLint maxLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); + + // The maxLength includes the NULL character + char* log = new char[maxLength]; + glGetProgramInfoLog(program, maxLength, &maxLength, log); + + hsStatusMessage(log); + delete[] log; + } + + sProgram = program; + } + + for (size_t oIdx = 0; oIdx < fClothingOutfits.size(); oIdx++) { + plClothingOutfit* co = fClothingOutfits[oIdx]; + if (co->fBase == nullptr || co->fBase->fBaseTexture == nullptr) + continue; + + plRenderTarget* rt = plRenderTarget::ConvertNoRef(co->fTargetLayer->GetTexture()); + if (rt != nullptr && co->fDirtyItems.Empty()) + // we've still got our valid RT from last frame and we have nothing to do. + continue; + + if (rt == nullptr) { + rt = IGetNextAvRT(); + + plGLMaterialShaderRef* mRef = static_cast(co->fMaterial->GetDeviceRef()); + if (mRef) + mRef->SetDirty(true); + + co->fTargetLayer->SetTexture(rt); + } + + PushRenderTarget(rt); + glViewport(0, 0, rt->GetWidth(), rt->GetHeight()); + glDepthRange(0.0, 1.0); + + glUseProgram(sProgram); + LOG_GL_ERROR_CHECK("Use Program failed"); + fDevice.SetCurrentProgram(sProgram); + + glUniform1i(0, 0); + glUniform4f(1, 1.f, 1.f, 1.f, 1.f); + glDepthFunc(GL_LEQUAL); + glDepthMask(GL_TRUE); + + float uOff = 0.5f / rt->GetWidth(); + float vOff = 0.5f / rt->GetHeight(); + + IDrawClothingQuad(-1.f, -1.f, 2.f, 2.f, uOff, vOff, co->fBase->fBaseTexture); + plClothingLayout *layout = plClothingMgr::GetClothingMgr()->GetLayout(co->fBase->fLayoutName); + + for (plClothingItem *item : co->fItems) { + for (size_t j = 0; j < item->fElements.size(); j++) { + for (int k = 0; k < plClothingElement::kLayerMax; k++) { + if (item->fTextures[j][k] == nullptr) + continue; + + plMipmap* itemBufferTex = item->fTextures[j][k]; + hsColorRGBA tint = co->GetItemTint(item, k); + if (k >= plClothingElement::kLayerSkinBlend1 && k <= plClothingElement::kLayerSkinLast) + tint.a = co->fSkinBlends[k - plClothingElement::kLayerSkinBlend1]; + + if (k == plClothingElement::kLayerBase) { + glBlendFunc(GL_ONE, GL_ZERO); + } else { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); + } + + glUniform4f(1, tint.r, tint.g, tint.b, tint.a); + + float screenW = (float)item->fElements[j]->fWidth / layout->fOrigWidth * 2.f; + float screenH = (float)item->fElements[j]->fHeight / layout->fOrigWidth * 2.f; + float screenX = (float)item->fElements[j]->fXPos / layout->fOrigWidth * 2.f - 1.f; + float screenY = (1.f - (float)item->fElements[j]->fYPos / layout->fOrigWidth) * 2.f - 1.f - screenH; + + IDrawClothingQuad(screenX, screenY, screenW, screenH, uOff, vOff, itemBufferTex); + } + } + } + + PopRenderTarget(); + co->fDirtyItems.Clear(); + } + + fView.fXformResetFlags = fView.kResetAll; + + fClothingOutfits.swap(fPrevClothingOutfits); +} + +void plGLPipeline::IDrawClothingQuad(float x, float y, float w, float h, float uOff, float vOff, plMipmap *tex) +{ + const uint32_t kVSize = sizeof(plAVTexVert); + + plGLTextureRef* ref = static_cast(tex->GetDeviceRef()); + if (!ref || ref->IsDirty()) + { + CheckTextureRef(tex); + ref = (plGLTextureRef*)tex->GetDeviceRef(); + } + + glActiveTexture(GL_TEXTURE0); + LOG_GL_ERROR_CHECK("Active Texture failed") + + glBindTexture(GL_TEXTURE_2D, ref->fRef); + LOG_GL_ERROR_CHECK("Bind Texture failed"); + + plAVTexVert ptr[4]; + plAVTexVert vert; + vert.fPos[0] = x; + vert.fPos[1] = y; + vert.fUv[0] = uOff; + vert.fUv[1] = 1.f + vOff; + + // P0 + ptr[2] = vert; + + // P1 + ptr[0] = vert; + ptr[0].fPos[0] += w; + ptr[0].fUv[0] += 1.f; + + // P2 + ptr[1] = vert; + ptr[1].fPos[0] += w; + ptr[1].fUv[0] += 1.f; + ptr[1].fPos[1] += h; + ptr[1].fUv[1] -= 1.f; + + // P3 + ptr[3] = vert; + ptr[3].fPos[1] += h; + ptr[3].fUv[1] -= 1.f; + + GLuint vbo; + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(ptr), ptr, GL_STATIC_DRAW); + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, kVSize, (void*)(sizeof(float) * 0)); + glEnableVertexAttribArray(0); + + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, kVSize, (void*)(sizeof(float) * 2)); + glEnableVertexAttribArray(1); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 6); + + LOG_GL_ERROR_CHECK("Render failed") + + glDeleteBuffers(1, &vbo); +} + + +bool plGLPipeline::ISoftwareVertexBlend(plDrawableSpans* drawable, const std::vector& visList) +{ + if (IsDebugFlagSet(plPipeDbg::kFlagNoSkinning)) + return true; + + if (drawable->GetSkinTime() == fRenderCnt) + return true; + + const hsBitVector& blendBits = drawable->GetBlendingSpanVector(); + + if (drawable->GetBlendingSpanVector().Empty()) { + // This sucker doesn't have any skinning spans anyway. Just return + drawable->SetSkinTime(fRenderCnt); + return true; + } + + plProfile_BeginTiming(Skin); + + // lock the data buffer + + // First, figure out which buffers we need to blend. + constexpr size_t kMaxBufferGroups = 20; + constexpr size_t kMaxVertexBuffers = 20; + static char blendBuffers[kMaxBufferGroups][kMaxVertexBuffers]; + memset(blendBuffers, 0, kMaxBufferGroups * kMaxVertexBuffers * sizeof(**blendBuffers)); + + hsAssert(kMaxBufferGroups >= drawable->GetNumBufferGroups(), "Bigger than we counted on num groups skin."); + + const std::vector& spans = drawable->GetSpanArray(); + for (int16_t idx : visList) { + if (blendBits.IsBitSet(idx)) { + const plVertexSpan &vSpan = *(plVertexSpan *)spans[idx]; + hsAssert(kMaxVertexBuffers > vSpan.fVBufferIdx, "Bigger than we counted on num buffers skin."); + + blendBuffers[vSpan.fGroupIdx][vSpan.fVBufferIdx] = 1; + drawable->SetBlendingSpanVectorBit(idx, false); + } + } + + // Now go through each of the group/buffer (= a real vertex buffer) pairs we found, + // and blend into it. We'll lock the buffer once, and then for each span that + // uses it, set the matrix palette and and then do the blend for that span. + // When we've done all the spans for a group/buffer, we unlock it and move on. + for (size_t i = 0; i < kMaxBufferGroups; i++) { + for (size_t j = 0; j < kMaxVertexBuffers; j++) { + if (blendBuffers[i][j]) { + // Found one. Do the lock. + plGLVertexBufferRef* vRef = static_cast(drawable->GetVertexRef(i, j)); + + hsAssert(vRef->fData, "Going into skinning with no place to put results!"); + + uint8_t* destPtr = vRef->fData; + + for (int16_t idx : visList) { + const plIcicle& span = *(plIcicle*)spans[idx]; + if ((span.fGroupIdx == i) && (span.fVBufferIdx == j)) { + plProfile_Inc(NumSkin); + + hsMatrix44* matrixPalette = drawable->GetMatrixPalette(span.fBaseMatrix); + matrixPalette[0] = span.fLocalToWorld; + + uint8_t* ptr = vRef->fOwner->GetVertBufferData(vRef->fIndex); + ptr += span.fVStartIdx * vRef->fOwner->GetVertexSize(); + IBlendVertsIntoBuffer((plSpan*)&span, + matrixPalette, span.fNumMatrices, + ptr, + vRef->fOwner->GetVertexFormat(), + vRef->fOwner->GetVertexSize(), + destPtr + span.fVStartIdx * vRef->fVertexSize, + vRef->fVertexSize, + span.fVLength, + span.fLocalUVWChans); + vRef->SetDirty(true); + } + } + // Unlock and move on. + } + } + } + + plProfile_EndTiming(Skin); + + if (drawable->GetBlendingSpanVector().Empty()) { + // Only do this if we've blended ALL of the spans. Thus, this becomes a trivial + // rejection for all the skinning flags being cleared + drawable->SetSkinTime(fRenderCnt); + } + + return true; +} + +static inline void ISkinVertexFPU(const hsMatrix44& xfm, float wgt, + const float* pt_src, float* pt_dst, + const float* vec_src, float* vec_dst) +{ + const float& m00 = xfm.fMap[0][0]; + const float& m01 = xfm.fMap[0][1]; + const float& m02 = xfm.fMap[0][2]; + const float& m03 = xfm.fMap[0][3]; + const float& m10 = xfm.fMap[1][0]; + const float& m11 = xfm.fMap[1][1]; + const float& m12 = xfm.fMap[1][2]; + const float& m13 = xfm.fMap[1][3]; + const float& m20 = xfm.fMap[2][0]; + const float& m21 = xfm.fMap[2][1]; + const float& m22 = xfm.fMap[2][2]; + const float& m23 = xfm.fMap[2][3]; + + // position + { + const float& srcX = pt_src[0]; + const float& srcY = pt_src[1]; + const float& srcZ = pt_src[2]; + + pt_dst[0] += (srcX * m00 + srcY * m01 + srcZ * m02 + m03) * wgt; + pt_dst[1] += (srcX * m10 + srcY * m11 + srcZ * m12 + m13) * wgt; + pt_dst[2] += (srcX * m20 + srcY * m21 + srcZ * m22 + m23) * wgt; + } + + // normal + { + const float& srcX = vec_src[0]; + const float& srcY = vec_src[1]; + const float& srcZ = vec_src[2]; + + vec_dst[0] += (srcX * m00 + srcY * m01 + srcZ * m02) * wgt; + vec_dst[1] += (srcX * m10 + srcY * m11 + srcZ * m12) * wgt; + vec_dst[2] += (srcX * m20 + srcY * m21 + srcZ * m22) * wgt; + } +} + +#ifdef HAVE_SSE3 +static inline void ISkinDpSSE3(const float* src, float* dst, const __m128& mc0, + const __m128& mc1, const __m128& mc2, const __m128& mwt) +{ + __m128 msr = _mm_load_ps(src); + __m128 _x = _mm_mul_ps(_mm_mul_ps(mc0, msr), mwt); + __m128 _y = _mm_mul_ps(_mm_mul_ps(mc1, msr), mwt); + __m128 _z = _mm_mul_ps(_mm_mul_ps(mc2, msr), mwt); + + __m128 hbuf1 = _mm_hadd_ps(_x, _y); + __m128 hbuf2 = _mm_hadd_ps(_z, _z); + hbuf1 = _mm_hadd_ps(hbuf1, hbuf2); + __m128 _dst = _mm_load_ps(dst); + _dst = _mm_add_ps(_dst, hbuf1); + _mm_store_ps(dst, _dst); +} +#endif // HAVE_SSE3 + +static inline void ISkinVertexSSE3(const hsMatrix44& xfm, float wgt, + const float* pt_src, float* pt_dst, + const float* vec_src, float* vec_dst) +{ +#ifdef HAVE_SSE3 + __m128 mc0 = _mm_load_ps(xfm.fMap[0]); + __m128 mc1 = _mm_load_ps(xfm.fMap[1]); + __m128 mc2 = _mm_load_ps(xfm.fMap[2]); + __m128 mwt = _mm_set_ps1(wgt); + + ISkinDpSSE3(pt_src, pt_dst, mc0, mc1, mc2, mwt); + ISkinDpSSE3(vec_src, vec_dst, mc0, mc1, mc2, mwt); +#endif // HAVE_SSE3 +} + +#ifdef HAVE_SSE41 +static inline void ISkinDpSSE41(const float* src, float* dst, const __m128& mc0, + const __m128& mc1, const __m128& mc2, const __m128& mwt) +{ + enum { DP_F4_X = 0xF1, DP_F4_Y = 0xF2, DP_F4_Z = 0xF4 }; + + __m128 msr = _mm_load_ps(src); + __m128 _r = _mm_dp_ps(msr, mc0, DP_F4_X); + _r = _mm_or_ps(_r, _mm_dp_ps(msr, mc1, DP_F4_Y)); + _r = _mm_or_ps(_r, _mm_dp_ps(msr, mc2, DP_F4_Z)); + + __m128 _dst = _mm_load_ps(dst); + _dst = _mm_add_ps(_dst, _mm_mul_ps(_r, mwt)); + _mm_store_ps(dst, _dst); +} +#endif // HAVE_SSE41 + +static inline void ISkinVertexSSE41(const hsMatrix44& xfm, float wgt, + const float* pt_src, float* pt_dst, + const float* vec_src, float* vec_dst) +{ +#ifdef HAVE_SSE41 + __m128 mc0 = _mm_load_ps(xfm.fMap[0]); + __m128 mc1 = _mm_load_ps(xfm.fMap[1]); + __m128 mc2 = _mm_load_ps(xfm.fMap[2]); + __m128 mwt = _mm_set_ps1(wgt); + + ISkinDpSSE41(pt_src, pt_dst, mc0, mc1, mc2, mwt); + ISkinDpSSE41(vec_src, vec_dst, mc0, mc1, mc2, mwt); +#endif // HAVE_SSE41 +} + +typedef void(*skin_vert_ptr)(const hsMatrix44&, float, const float*, float*, const float*, float*); + +template +static void IBlendVertBuffer(plSpan* span, hsMatrix44* matrixPalette, int numMatrices, + const uint8_t* src, uint8_t format, uint32_t srcStride, + uint8_t* dest, uint32_t destStride, uint32_t count, + uint16_t localUVWChans) +{ + alignas(16) float pt_buf[] = { 0.f, 0.f, 0.f, 1.f }; + alignas(16) float vec_buf[] = { 0.f, 0.f, 0.f, 0.f }; + hsPoint3* pt = reinterpret_cast(pt_buf); + hsVector3* vec = reinterpret_cast(vec_buf); + + uint32_t indices; + float weights[4]; + + // Dropped support for localUVWChans at templatization of code + hsAssert(localUVWChans == 0, "support for skinned UVWs dropped. reimplement me?"); + const size_t uvChanSize = plGBufferGroup::CalcNumUVs(format) * sizeof(float) * 3; + uint8_t numWeights = (format & plGBufferGroup::kSkinWeightMask) >> 4; + + for (uint32_t i = 0; i < count; ++i) { + // Extract data + src = inlExtract(src, pt); + + float weightSum = 0.f; + for (uint8_t j = 0; j < numWeights; ++j) { + src = inlExtract(src, &weights[j]); + weightSum += weights[j]; + } + weights[numWeights] = 1.f - weightSum; + + if (format & plGBufferGroup::kSkinIndices) + src = inlExtract(src, &indices); + else + indices = 1 << 8; + src = inlExtract(src, vec); + + // Destination buffers (float4 for SSE alignment) + alignas(16) float destNorm_buf[] = { 0.f, 0.f, 0.f, 0.f }; + alignas(16) float destPt_buf[] = { 0.f, 0.f, 0.f, 1.f }; + + // Blend + for (uint32_t j = 0; j < numWeights + 1; ++j) { + if (weights[j]) + T(matrixPalette[indices & 0xFF], weights[j], pt_buf, destPt_buf, vec_buf, destNorm_buf); + indices >>= 8; + } + // Probably don't really need to renormalize this. There errors are + // going to be subtle and "smooth". + /* hsFastMath::NormalizeAppr(destNorm); */ + + // Slam data into position now + dest = inlStuff(dest, reinterpret_cast(destPt_buf)); + dest = inlStuff(dest, reinterpret_cast(destNorm_buf)); + + // Jump past colors and UVws + dest += sizeof(uint32_t) * 2 + uvChanSize; + src += sizeof(uint32_t) * 2 + uvChanSize; + } +} + +// CPU-optimized functions requiring dispatch +hsCpuFunctionDispatcher plGLPipeline::blend_vert_buffer { + &IBlendVertBuffer, + nullptr, // SSE1 + nullptr, // SSE2 + &IBlendVertBuffer, + nullptr, // SSSE3 + &IBlendVertBuffer +}; diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLPipeline.h b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLPipeline.h index 55c895656e..b5eb6312dc 100644 --- a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLPipeline.h +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLPipeline.h @@ -46,6 +46,11 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plPipeline/pl3DPipeline.h" #include "plPipeline/hsG3DDeviceSelector.h" +class plIcicle; +class plGLMaterialShaderRef; +class plGLVertexBufferRef; +class plPlate; + class plGLEnumerate { public: @@ -60,10 +65,19 @@ class plGLEnumerate class plGLPipeline : public pl3DPipeline { friend class plGLPlateManager; + friend class plGLDevice; + friend class plGLMaterialShaderRef; + +protected: + typedef void(*blend_vert_buffer_ptr)(plSpan*, hsMatrix44*, int, const uint8_t*, uint8_t , uint32_t, uint8_t*, uint32_t, uint32_t, uint16_t); + static hsCpuFunctionDispatcher blend_vert_buffer; + + plGLMaterialShaderRef* fMatRefList; + plGLRenderTargetRef* fRenderTargetRefList; public: plGLPipeline(hsWindowHndl display, hsWindowHndl window, const hsG3DDeviceModeRecord *devMode); - virtual ~plGLPipeline() = default; + virtual ~plGLPipeline(); CLASSNAME_REGISTER(plGLPipeline); GETINTERFACE_ANY(plGLPipeline, plPipeline); @@ -76,33 +90,20 @@ class plGLPipeline : public pl3DPipeline /*** VIRTUAL METHODS ***/ bool PreRender(plDrawable* drawable, std::vector& visList, plVisMgr* visMgr=nullptr) override; bool PrepForRender(plDrawable* drawable, std::vector& visList, plVisMgr* visMgr=nullptr) override; - void Render(plDrawable* d, const std::vector& visList) override; plTextFont* MakeTextFont(ST::string face, uint16_t size) override; - void CheckVertexBufferRef(plGBufferGroup* owner, uint32_t idx) override; - void CheckIndexBufferRef(plGBufferGroup* owner, uint32_t idx) override; bool OpenAccess(plAccessSpan& dst, plDrawableSpans* d, const plVertexSpan* span, bool readOnly) override; bool CloseAccess(plAccessSpan& acc) override; - void CheckTextureRef(plLayerInterface* lay) override; void PushRenderRequest(plRenderRequest* req) override; void PopRenderRequest(plRenderRequest* req) override; void ClearRenderTarget(plDrawable* d) override; void ClearRenderTarget(const hsColorRGBA* col = nullptr, const float* depth = nullptr) override; hsGDeviceRef* MakeRenderTargetRef(plRenderTarget* owner) override; - void PushRenderTarget(plRenderTarget* target) override; - plRenderTarget* PopRenderTarget() override; bool BeginRender() override; bool EndRender() override; void RenderScreenElements() override; bool IsFullScreen() const override; - uint32_t ColorDepth() const override; void Resize(uint32_t width, uint32_t height) override; - bool CheckResources() override; void LoadResources() override; - void SetZBiasScale(float scale) override; - float GetZBiasScale() const override; - void SetWorldToCamera(const hsMatrix44& w2c, const hsMatrix44& c2w) override; - void RefreshScreenMatrices() override; - void SubmitClothingOutfit(plClothingOutfit* co) override; bool SetGamma(float eR, float eG, float eB) override; bool SetGamma(const uint16_t* const tabR, const uint16_t* const tabG, const uint16_t* const tabB) override; bool CaptureScreen(plMipmap* dest, bool flipVertical = false, uint16_t desiredWidth = 0, uint16_t desiredHeight = 0) override; @@ -113,6 +114,64 @@ class plGLPipeline : public pl3DPipeline void ResetDisplayDevice(int Width, int Height, int ColorDepth, bool Windowed, int NumAASamples, int MaxAnisotropicSamples, bool vSync = false ) override; void RenderSpans(plDrawableSpans* ice, const std::vector& visList) override; +protected: + void ISetupTransforms(plDrawableSpans* drawable, const plSpan& span, plGLMaterialShaderRef* mRef, hsMatrix44& lastL2W); + void IRenderBufferSpan(const plIcicle& span, hsGDeviceRef* vb, hsGDeviceRef* ib, hsGMaterial* material, uint32_t vStart, uint32_t vLength, uint32_t iStart, uint32_t iLength); + + /** + * Only software skinned objects, dynamic decals, and particle systems + * currently use the dynamic vertex buffer. + */ + bool IRefreshDynVertices(plGBufferGroup* group, plGLVertexBufferRef* vRef); + + /** + * Make sure the buffers underlying this span are ready to be rendered. + * Meaning that the underlying GL buffers are in sync with the plasma + * buffers. + */ + bool ICheckDynBuffers(plDrawableSpans* drawable, plGBufferGroup* group, const plSpan* span); + + void IHandleZMode(hsGMatState flags); + void IHandleBlendMode(hsGMatState flags); + + /** + * Tests and sets the current winding order cull mode (CW, CCW, or none). + * Will reverse the cull mode as necessary for left handed camera or local + * to world transforms. + */ + void ISetCullMode(); + + void ICalcLighting(plGLMaterialShaderRef* mRef, const plLayerInterface* currLayer, const plSpan* currSpan); + void ISelectLights(const plSpan* span, plGLMaterialShaderRef* mRef, bool proj = false); + void IEnableLight(plGLMaterialShaderRef* mRef, size_t i, plLightInfo* light); + void IDisableLight(plGLMaterialShaderRef* mRef, size_t i); + void IScaleLight(plGLMaterialShaderRef* mRef, size_t i, float scale); + void IDrawPlate(plPlate* plate); + void IPreprocessAvatarTextures(); + void IDrawClothingQuad(float x, float y, float w, float h, float uOff, float vOff, plMipmap *tex); + + /** + * Emulate matrix palette operations in software. + * + * The big difference between the hardware and software versions is we only + * want to lock the vertex buffer once and blend all the verts we're going + * to in software, so the vertex blend happens once for an entire drawable. + * In hardware, we want the opposite, to break it into managable chunks, + * manageable meaning few enough matrices to fit into hardware registers. + * So for hardware version, we set up our palette, draw a span or few, + * setup our matrix palette with new matrices, draw, repeat. + */ + bool ISoftwareVertexBlend(plDrawableSpans* drawable, const std::vector& visList); + + /** + * Given a pointer into a buffer of verts that have blending data in the + * plVertCoder format, blends them into the destination buffer given + * without the blending info. + */ + void IBlendVertsIntoBuffer(plSpan* span, hsMatrix44* matrixPalette, int numMatrices, const uint8_t* src, uint8_t format, uint32_t srcStride, uint8_t* dest, uint32_t destStride, uint32_t count, uint16_t localUVWChans) { + blend_vert_buffer.call(span, matrixPalette, numMatrices, src, format, srcStride, dest, destStride, count, localUVWChans); + }; + private: static plGLEnumerate enumerator; }; diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLPlateManager.cpp b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLPlateManager.cpp index 58d68cd765..8c96bb91ab 100644 --- a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLPlateManager.cpp +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLPlateManager.cpp @@ -40,16 +40,187 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com *==LICENSE==*/ +#include "plGLMaterialShaderRef.h" #include "plGLPlateManager.h" #include "plGLPipeline.h" plGLPlateManager::plGLPlateManager(plGLPipeline* pipe) - : plPlateManager(pipe) + : plPlateManager(pipe), fBuffers { 0, 0, 0 } {} plGLPlateManager::~plGLPlateManager() -{} +{ + IReleaseGeometry(); +} + +void plGLPlateManager::ICreateGeometry() +{ + plPlateVertex verts[4]; + + verts[0].fPoint.Set(-0.5f, -0.5f, 0.0f); + verts[0].fNormal.Set(0.0f, 0.0f, 1.0f); + verts[0].fColor = 0xffffffff; + verts[0].fUV.Set(0.0f, 0.0f, 0.0f); + + verts[1].fPoint.Set(-0.5f, 0.5f, 0.0f); + verts[1].fNormal.Set(0.0f, 0.0f, 1.0f); + verts[1].fColor = 0xffffffff; + verts[1].fUV.Set(0.0f, 1.0f, 0.0f); + + verts[2].fPoint.Set(0.5f, -0.5f, 0.0f); + verts[2].fNormal.Set(0.0f, 0.0f, 1.0f); + verts[2].fColor = 0xffffffff; + verts[2].fUV.Set(1.0f, 0.0f, 0.0f); + + verts[3].fPoint.Set(0.5f, 0.5f, 0.0f); + verts[3].fNormal.Set(0.0f, 0.0f, 1.0f); + verts[3].fColor = 0xffffffff; + verts[3].fUV.Set(1.0f, 1.0f, 0.0f); + + uint16_t indices[6] = {0, 1, 2, 1, 2, 3}; + + + GLuint vbo, ibo, vao = 0; + + if (plGLVersion() >= 45) { + glCreateBuffers(1, &vbo); + glObjectLabel(GL_BUFFER, vbo, -1, "plPlate/VBO"); + glNamedBufferStorage(vbo, sizeof(verts), verts, 0); + + glCreateBuffers(1, &ibo); + glObjectLabel(GL_BUFFER, ibo, -1, "plPlate/IBO"); + glNamedBufferStorage(ibo, sizeof(indices), indices, 0); + + glCreateVertexArrays(1, &vao); + glObjectLabel(GL_VERTEX_ARRAY, vao, -1, "plPlate/VAO"); + + glVertexArrayVertexBuffer(vao, 0, vbo, 0, sizeof(plPlateVertex)); + glVertexArrayElementBuffer(vao, ibo); + + glEnableVertexArrayAttrib(vao, kVtxPosition); + glEnableVertexArrayAttrib(vao, kVtxNormal); + glEnableVertexArrayAttrib(vao, kVtxColor); + glEnableVertexArrayAttrib(vao, kVtxUVWSrc); + + glVertexArrayAttribFormat(vao, kVtxPosition, 3, GL_FLOAT, GL_FALSE, offsetof(plPlateVertex, fPoint)); + glVertexArrayAttribFormat(vao, kVtxNormal, 3, GL_FLOAT, GL_FALSE, offsetof(plPlateVertex, fNormal)); + glVertexArrayAttribFormat(vao, kVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, offsetof(plPlateVertex, fColor)); + glVertexArrayAttribFormat(vao, kVtxUVWSrc, 3, GL_FLOAT, GL_FALSE, offsetof(plPlateVertex, fUV)); + + glVertexArrayAttribBinding(vao, kVtxPosition, 0); + glVertexArrayAttribBinding(vao, kVtxNormal, 0); + glVertexArrayAttribBinding(vao, kVtxColor, 0); + glVertexArrayAttribBinding(vao, kVtxUVWSrc, 0); + } else { + if (plGLVersion() >= 30) { + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + } + + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); + + glGenBuffers(1, &ibo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + + if (plGLVersion() >= 30) { + glEnableVertexAttribArray(kVtxPosition); + glVertexAttribPointer(kVtxPosition, 3, GL_FLOAT, GL_FALSE, sizeof(plPlateVertex), (void*)(sizeof(float) * 0)); + + glEnableVertexAttribArray(kVtxNormal); + glVertexAttribPointer(kVtxNormal, 3, GL_FLOAT, GL_FALSE, sizeof(plPlateVertex), (void*)(sizeof(float) * 3)); + + glEnableVertexAttribArray(kVtxColor); + glVertexAttribPointer(kVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(plPlateVertex), (void*)(sizeof(float) * 3 * 2)); + + glEnableVertexAttribArray(kVtxUVWSrc); + glVertexAttribPointer(kVtxUVWSrc, 3, GL_FLOAT, GL_FALSE, sizeof(plPlateVertex), (void*)((sizeof(float) * 3 * 2) + sizeof(uint32_t))); + + glBindVertexArray(0); + } + } + + fBuffers = {vbo, ibo, vao }; +} + +void plGLPlateManager::IReleaseGeometry() +{ + if (plGLVersion() >= 30 && fBuffers.ARef) { + if (plGLVersion() >= 45) { + glDisableVertexArrayAttrib(fBuffers.ARef, kVtxPosition); + glDisableVertexArrayAttrib(fBuffers.ARef, kVtxNormal); + glDisableVertexArrayAttrib(fBuffers.ARef, kVtxColor); + glDisableVertexArrayAttrib(fBuffers.ARef, kVtxUVWSrc); + } + + glDeleteVertexArrays(1, &fBuffers.ARef); + fBuffers.ARef = 0; + } + + if (fBuffers.VRef) { + glDeleteBuffers(1, &fBuffers.VRef); + fBuffers.VRef = 0; + } + + if (fBuffers.IRef) { + glDeleteBuffers(1, &fBuffers.IRef); + fBuffers.IRef = 0; + } +} void plGLPlateManager::IDrawToDevice(plPipeline* pipe) -{} +{ + plGLPipeline* glPipe = static_cast(pipe); + uint32_t scrnWidthDiv2 = fOwner->Width() >> 1; + uint32_t scrnHeightDiv2 = fOwner->Height() >> 1; + plPlate* plate = nullptr; + + if (fBuffers.VRef == 0 || fBuffers.IRef == 0) { + ICreateGeometry(); + + if (fBuffers.VRef == 0 || fBuffers.IRef == 0) + return; + } + + if (plGLVersion() >= 30) { + if (fBuffers.ARef == 0) + return; + + glBindVertexArray(fBuffers.ARef); + } else { + glBindBuffer(GL_ARRAY_BUFFER, fBuffers.VRef); + + glVertexAttribPointer(kVtxPosition, 3, GL_FLOAT, GL_FALSE, sizeof(plPlateVertex), (void*)(sizeof(float) * 0)); + glEnableVertexAttribArray(kVtxPosition); + + glVertexAttribPointer(kVtxNormal, 3, GL_FLOAT, GL_FALSE, sizeof(plPlateVertex), (void*)(sizeof(float) * 3)); + glEnableVertexAttribArray(kVtxNormal); + + glVertexAttribPointer(kVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(plPlateVertex), (void*)(sizeof(float) * 3 * 2)); + glEnableVertexAttribArray(kVtxColor); + + glVertexAttribPointer(kVtxUVWSrc, 3, GL_FLOAT, GL_FALSE, sizeof(plPlateVertex), (void*)((sizeof(float) * 3 * 2) + sizeof(uint32_t))); + glEnableVertexAttribArray(kVtxUVWSrc); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, fBuffers.IRef); + } + + glPipe->fDevice.SetWorldToCameraMatrix(hsMatrix44::IdentityMatrix()); + + GLboolean cull = glIsEnabled(GL_CULL_FACE); + if (cull) + glDisable(GL_CULL_FACE); + + for (plate = fPlates; plate != nullptr; plate = plate->GetNext()) { + if (plate->IsVisible()) + glPipe->IDrawPlate(plate); + } + + if (cull) + glEnable(GL_CULL_FACE); + if (plGLVersion() >= 30) + glBindVertexArray(0); +} diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLPlateManager.h b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLPlateManager.h index f5e024e39d..5ce5779b42 100644 --- a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLPlateManager.h +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLPlateManager.h @@ -43,6 +43,9 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #ifndef _plGLPlateManager_inc_ #define _plGLPlateManager_inc_ +#include "plGLDeviceRef.h" + +#include "hsGeometry3.h" #include "plPipeline/plPlates.h" class plGLPipeline; @@ -56,8 +59,28 @@ class plGLPlateManager : public plPlateManager virtual ~plGLPlateManager(); protected: + struct plPlateVertex + { + hsPoint3 fPoint; + hsVector3 fNormal; + uint32_t fColor; + hsPoint3 fUV; + }; + + struct plPlateBuffers + { + GLuint VRef; + GLuint IRef; + GLuint ARef; + }; + + plPlateBuffers fBuffers; + plGLPlateManager(plGLPipeline* pipe); + void ICreateGeometry(); + void IReleaseGeometry(); + void IDrawToDevice(plPipeline* pipe) override; }; diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLTextFont.cpp b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLTextFont.cpp new file mode 100644 index 0000000000..1b2225f65c --- /dev/null +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLTextFont.cpp @@ -0,0 +1,299 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "plGLTextFont.h" + +#include "HeadSpin.h" + +#include "plGLPipeline.h" + +// Following number needs to be at least: 64 chars max in plTextFont drawn at any one time +// * 4 primitives per char max (for bold text) +// * 3 verts per primitive + +//const uint32_t kNumVertsInBuffer(32768); +const uint32_t kNumVertsInBuffer(4608); + +enum plGLTextFontShaderConstants : GLuint { + kVtxPosition = 0, + kVtxColor = 1, + kVtxUVWSrc = 2 +}; + +plGLTextFont::plGLTextFont(plPipeline* pipe, plGLDevice* device) + : plTextFont(pipe), fTexture(), fBuffer(), fState(), fShader(), fBufferCursor() +{ +} + +plGLTextFont::~plGLTextFont() +{ + DestroyObjects(); +} + +void plGLTextFont::ICreateTexture(uint16_t* data) +{ + if (fTexture) + glDeleteTextures(1, &fTexture); + + glGenTextures(1, &fTexture); + glBindTexture(GL_TEXTURE_2D, fTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fTextureWidth, fTextureHeight, 0, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4, data); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + if (plGLVersion() >= 43) { + glObjectLabel(GL_TEXTURE, fTexture, -1, fFace.c_str()); + } +} + +static const char* TEXTFONT_VERTEX_SHADER_STRING = R"(#version 430 + +layout(location = 0) in vec3 aVtxPosition; +layout(location = 1) in vec4 aVtxColor; +layout(location = 2) in highp vec3 aVtxUV; + +layout(location = 1) uniform vec2 uPipeSize; + +out vec4 vVtxColor; +out vec3 vVtxUV; + +void main() { + mat4 projMatrix = mat4(1.0); + projMatrix[0][0] = 2.0 / uPipeSize.x; + projMatrix[1][1] = -2.0 / uPipeSize.y; + projMatrix[3][0] = -1.0; + projMatrix[3][1] = 1.0; + + vVtxColor = aVtxColor.bgra; + vVtxUV = aVtxUV; + + gl_Position = projMatrix * vec4(aVtxPosition, 1.0); +})"; + +static const char* TEXTFONT_FRAGMENT_SHADER_STRING = R"(#version 430 +precision mediump float; + +layout(location = 0) uniform sampler2D uTex; + +in vec4 vVtxColor; +in highp vec3 vVtxUV; +out vec4 fragColor; + +void main() { + fragColor = texture(uTex, vec2(vVtxUV.x, vVtxUV.y)) * vVtxColor; +})"; + +void plGLTextFont::IInitStateBlocks() +{ + GLuint vshader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vshader, 1, &TEXTFONT_VERTEX_SHADER_STRING, nullptr); + glCompileShader(vshader); + LOG_GL_ERROR_CHECK("Vertex Shader compile failed"); + + GLuint fshader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fshader, 1, &TEXTFONT_FRAGMENT_SHADER_STRING, nullptr); + glCompileShader(fshader); + LOG_GL_ERROR_CHECK("Vertex Shader compile failed"); + + GLuint program = glCreateProgram(); + LOG_GL_ERROR_CHECK("Create Program failed"); + + if (plGLVersion() >= 43) { + const char* name = "TextFont"; + glObjectLabel(GL_PROGRAM, program, strlen(name), name); + } + + glAttachShader(program, vshader); + LOG_GL_ERROR_CHECK("Attach Vertex Shader failed"); + + glAttachShader(program, fshader); + LOG_GL_ERROR_CHECK("Attach Fragment Shader failed"); + + glLinkProgram(program); + LOG_GL_ERROR_CHECK("Program Link failed"); + + GLint isLinked = 0; + glGetProgramiv(program, GL_LINK_STATUS, &isLinked); + if (isLinked == GL_FALSE) + { + GLint maxLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); + + // The maxLength includes the NULL character + char* log = new char[maxLength]; + glGetProgramInfoLog(program, maxLength, &maxLength, log); + + LOG_GL_ERROR_CHECK(log); + delete[] log; + } + + fShader = program; + + if (plGLVersion() >= 30) { + glGenVertexArrays(1, &fState); + //glBindVertexArray(fState); + } + + /* + glGenBuffers(1, &fBuffer); + glBindBuffer(GL_ARRAY_BUFFER, fBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(plFontVertex) * kNumVertsInBuffer, nullptr, GL_STATIC_DRAW); + + if (plGLVersion() >= 30) { + glEnableVertexAttribArray(kVtxPosition); + glVertexAttribPointer(kVtxPosition, 3, GL_FLOAT, GL_FALSE, sizeof(plFontVertex), (void*)(sizeof(float) * 0)); + + glEnableVertexAttribArray(kVtxColor); + glVertexAttribPointer(kVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(plFontVertex), (void*)(sizeof(float) * 3)); + + glEnableVertexAttribArray(kVtxUVWSrc); + glVertexAttribPointer(kVtxUVWSrc, 3, GL_FLOAT, GL_FALSE, sizeof(plFontVertex), (void*)((sizeof(float) * 3) + sizeof(uint32_t))); + + glBindVertexArray(0); + } + */ +} + +void plGLTextFont::IDrawPrimitive(uint32_t count, plFontVertex* array) +{ + if (count == 0 || array == nullptr) + return; + + GLuint vbo; + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(plFontVertex) * 3 * count, array, GL_STATIC_DRAW); + + glEnableVertexAttribArray(kVtxPosition); + glVertexAttribPointer(kVtxPosition, 3, GL_FLOAT, GL_FALSE, sizeof(plFontVertex), (void*)(sizeof(float) * 0)); + + glEnableVertexAttribArray(kVtxColor); + glVertexAttribPointer(kVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(plFontVertex), (void*)(sizeof(float) * 3)); + + glEnableVertexAttribArray(kVtxUVWSrc); + glVertexAttribPointer(kVtxUVWSrc, 3, GL_FLOAT, GL_FALSE, sizeof(plFontVertex), (void*)(sizeof(float) * 3 + sizeof(uint32_t))); + + glDrawArrays(GL_TRIANGLES, 0, count * 3); + + LOG_GL_ERROR_CHECK("Render failed") + + glDeleteBuffers(1, &vbo); +} + +void plGLTextFont::IDrawLines(uint32_t count, plFontVertex* array) +{ + if (count == 0 || array == nullptr) + return; + + GLuint vbo; + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(plFontVertex) * 3 * count, array, GL_STATIC_DRAW); + + glDrawArrays(GL_LINES, 0, count * 3); + + LOG_GL_ERROR_CHECK("Render failed") + + glDeleteBuffers(1, &vbo); +} + +void plGLTextFont::FlushDraws() +{ +} + +void plGLTextFont::SaveStates() +{ + if (!fInitialized) + IInitObjects(); + + if (plGLVersion() >= 30 && fState) + glBindVertexArray(fState); + + glActiveTexture(GL_TEXTURE0); + LOG_GL_ERROR_CHECK("Active Texture failed") + + glBindTexture(GL_TEXTURE_2D, fTexture); + LOG_GL_ERROR_CHECK("Bind Texture failed"); + + glViewport(0, 0, fPipe->Width(), fPipe->Height()); + glDepthRange(0.0, 1.0); + + glUseProgram(fShader); + LOG_GL_ERROR_CHECK("Use Program failed"); + + glUniform1i(0, 0); + glUniform2f(1, float(fPipe->Width()), float(fPipe->Height())); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthFunc(GL_ALWAYS); + glDepthMask(GL_TRUE); + glDisable(GL_CULL_FACE); +} + +void plGLTextFont::RestoreStates() +{ + if (plGLVersion() >= 30) + glBindVertexArray(0); + + glUseProgram(0); +} + +void plGLTextFont::DestroyObjects() +{ + if (fTexture) + glDeleteTextures(1, &fTexture); + fTexture = 0; + + if (plGLVersion() >= 30 && fState) { + if (plGLVersion() >= 45) { + glDisableVertexArrayAttrib(fState, kVtxPosition); + glDisableVertexArrayAttrib(fState, kVtxColor); + glDisableVertexArrayAttrib(fState, kVtxUVWSrc); + } + + glDeleteVertexArrays(1, &fState); + } + fState = 0; + + if (fBuffer) + glDeleteBuffers(1, &fBuffer); + fBuffer = 0; +} diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plGLTextFont.h b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLTextFont.h new file mode 100644 index 0000000000..fd90dc322c --- /dev/null +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plGLTextFont.h @@ -0,0 +1,75 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _plGLTextFont_h +#define _plGLTextFont_h + +#include "plGLDeviceRef.h" +#include "plPipeline/plTextFont.h" + +class plPipeline; +class plGLDevice; + +class plGLTextFont : public plTextFont +{ +protected: + GLuint fTexture; + GLuint fBuffer; + GLuint fState; + GLuint fShader; + uint32_t fBufferCursor; + + void ICreateTexture(uint16_t* data) override; + void IInitStateBlocks() override; + void IDrawPrimitive(uint32_t count, plFontVertex* array) override; + void IDrawLines(uint32_t count, plFontVertex* array) override; + +public: + plGLTextFont(plPipeline* pipe, plGLDevice* device); + ~plGLTextFont(); + + void FlushDraws() override; + void SaveStates() override; + void RestoreStates() override; + void DestroyObjects() override; +}; + +#endif // _plGLTextFont_h diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plWGLDevice.cpp b/Sources/Plasma/FeatureLib/pfGLPipeline/plWGLDevice.cpp new file mode 100644 index 0000000000..702793b354 --- /dev/null +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plWGLDevice.cpp @@ -0,0 +1,212 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "plWGLDevice.h" + +#ifdef HS_BUILD_FOR_WIN32 + +bool plWGLDevice::Enumerate(hsG3DDeviceRecord& record) +{ + bool result = false; + ATOM cls = 0; + HWND wnd = nullptr; + HDC dc = nullptr; + HGLRC ctx = nullptr; + + do { + WNDCLASSW tempClass = {}; + tempClass.lpfnWndProc = DefWindowProc; + tempClass.hInstance = GetModuleHandle(nullptr); + tempClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + tempClass.lpszClassName = L"GLTestClass"; + tempClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + + cls = RegisterClassW(&tempClass); + if (!cls) + break; + + wnd = CreateWindowExW(WS_EX_NOACTIVATE, reinterpret_cast(cls), + L"OpenGL Test Window", + WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + nullptr, nullptr, GetModuleHandle(nullptr), nullptr); + if (!wnd) + break; + + dc = GetDC(wnd); + if (!dc) + break; + + PIXELFORMATDESCRIPTOR pfd = {}; + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_GENERIC_ACCELERATED | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + pfd.cDepthBits = 24; + pfd.iLayerType = PFD_MAIN_PLANE; + + int format = ChoosePixelFormat(dc, &pfd); + if (!format) + break; + + if (!SetPixelFormat(dc, format, &pfd)) + break; + + ctx = wglCreateContext(dc); + if (!ctx) + break; + + if (!wglMakeCurrent(dc, ctx)) + break; + + if (epoxy_gl_version() < 33) + break; + + record.SetG3DDeviceType(hsG3DDeviceSelector::kDevTypeOpenGL); + record.SetDriverName("opengl32.dll"); + record.SetDeviceDesc(reinterpret_cast(glGetString(GL_RENDERER))); + record.SetDriverDesc(reinterpret_cast(glGetString(GL_VENDOR))); + record.SetDriverVersion(reinterpret_cast(glGetString(GL_VERSION))); + + result = true; + } while (0); + + // Cleanup: + if (ctx) { + wglMakeCurrent(nullptr, nullptr); + wglDeleteContext(ctx); + } + + if (dc) + ReleaseDC(wnd, dc); + + if (wnd) + DestroyWindow(wnd); + + if (cls) + UnregisterClassW(reinterpret_cast(cls), GetModuleHandle(nullptr)); + + return result; +} + + +plWGLDevice* plWGLDevice::TryInit(hsWindowHndl window, hsWindowHndl device, ST::string& error) +{ + HDC dc = static_cast((HANDLE)device); + HGLRC ctx = nullptr; + + do { + PIXELFORMATDESCRIPTOR pfd{}; + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_GENERIC_ACCELERATED | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + pfd.cDepthBits = 24; + pfd.iLayerType = PFD_MAIN_PLANE; + + int format = ChoosePixelFormat(dc, &pfd); + if (!format) { + error = ST_LITERAL("Could not find appropriate pixel config"); + break; + } + + if (!SetPixelFormat(dc, format, &pfd)) { + error = ST_LITERAL("Could not set appropriate pixel config"); + break; + } + + ctx = wglCreateContext(dc); + if (!ctx) { + error = ST_LITERAL("Unable to create rendering context"); + break; + } + + if (!wglMakeCurrent(dc, ctx)) { + error = ST_LITERAL("Failed to attach WGL context to surface"); + break; + } + + // Successfully initialized + return new plWGLDevice(window, device, ctx); + } while (0); + + // Cleanup for failure case: + if (ctx) { + wglMakeCurrent(nullptr, nullptr); + wglDeleteContext(ctx); + } + return nullptr; +} + + +plWGLDevice::plWGLDevice(hsWindowHndl window, hsWindowHndl device, HGLRC context) + : plGLDeviceImpl(window, device), fContext(context) +{ } + +void plWGLDevice::Shutdown() +{ + wglMakeCurrent(nullptr, nullptr); + wglDeleteContext(fContext); + fContext = nullptr; +} + +bool plWGLDevice::BeginRender(ST::string& error) +{ + // Best practice, apparently, is to get and release the DC every time we need it. + // A DC is only valid on one thread at a time. + fDevice = static_cast((HANDLE)GetDC(fWindow)); + + return true; +} + +bool plWGLDevice::EndRender(ST::string& error) +{ + SwapBuffers(static_cast((HANDLE)fDevice)); + + ReleaseDC(fWindow, static_cast((HANDLE)fDevice)); + fDevice = nullptr; + return true; +} + +#endif // HS_BUILD_FOR_WIN32 diff --git a/Sources/Plasma/FeatureLib/pfGLPipeline/plWGLDevice.h b/Sources/Plasma/FeatureLib/pfGLPipeline/plWGLDevice.h new file mode 100644 index 0000000000..fd8cbae427 --- /dev/null +++ b/Sources/Plasma/FeatureLib/pfGLPipeline/plWGLDevice.h @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _plWGLDevice_h_ +#define _plWGLDevice_h_ + +#include "HeadSpin.h" +#include "plGLDevice.h" + +#ifdef HS_BUILD_FOR_WIN32 +#include "hsWindows.h" +#include "plPipeline/hsG3DDeviceSelector.h" +#include + +class plWGLDevice : public plGLDeviceImpl +{ +protected: + HGLRC fContext; + + plWGLDevice(hsWindowHndl window, hsWindowHndl device, HGLRC context); + +public: + static bool Enumerate(hsG3DDeviceRecord& record); + static plWGLDevice* TryInit(hsWindowHndl window, hsWindowHndl device, ST::string& error); + + void Shutdown() override; + bool BeginRender(ST::string& error) override; + bool EndRender(ST::string& error) override; +}; + +#endif // HS_BUILD_FOR_WIN32 + +#endif // _plWGLDevice_h_ + diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalDevice.cpp b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalDevice.cpp index b117fcd5c5..93c2330578 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalDevice.cpp +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalDevice.cpp @@ -733,7 +733,7 @@ void plMetalDevice::FillIndexBufferRef(plMetalDevice::IndexBufferRef* iRef, plGB iRef->SetDirty(false); } -void plMetalDevice::SetupTextureRef(plBitmap* img, plMetalDevice::TextureRef* tRef) +void plMetalDevice::SetupTextureRef(plLayerInterface* layer, plBitmap* img, plMetalDevice::TextureRef* tRef) { tRef->fOwner = img; @@ -908,7 +908,7 @@ void plMetalDevice::PopulateTexture(plMetalDevice::TextureRef* tRef, plMipmap* i tRef->SetDirty(false); } -void plMetalDevice::MakeTextureRef(plMetalDevice::TextureRef* tRef, plMipmap* img) +void plMetalDevice::MakeTextureRef(plMetalDevice::TextureRef* tRef, plLayerInterface* layer, plMipmap* img) { if (!img->GetImage()) { return; @@ -944,7 +944,7 @@ void plMetalDevice::MakeTextureRef(plMetalDevice::TextureRef* tRef, plMipmap* im tRef->SetDirty(false); } -void plMetalDevice::MakeCubicTextureRef(plMetalDevice::TextureRef* tRef, plCubicEnvironmap* img) +void plMetalDevice::MakeCubicTextureRef(plMetalDevice::TextureRef* tRef, plLayerInterface* layer, plCubicEnvironmap* img) { MTL::TextureDescriptor* descriptor = MTL::TextureDescriptor::textureCubeDescriptor(tRef->fFormat, img->GetFace(0)->GetWidth(), tRef->fLevels != 0); diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalDevice.h b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalDevice.h index 4aec911ed0..e1e971d6c6 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalDevice.h +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalDevice.h @@ -136,10 +136,10 @@ class plMetalDevice void CheckIndexBuffer(IndexBufferRef* iRef); void FillIndexBufferRef(IndexBufferRef* iRef, plGBufferGroup* owner, uint32_t idx); - void SetupTextureRef(plBitmap* img, TextureRef* tRef); + void SetupTextureRef(plLayerInterface* layer, plBitmap* img, TextureRef* tRef); void CheckTexture(TextureRef* tRef); - void MakeTextureRef(TextureRef* tRef, plMipmap* img); - void MakeCubicTextureRef(TextureRef* tRef, plCubicEnvironmap* img); + void MakeTextureRef(TextureRef* tRef, plLayerInterface* layer, plMipmap* img); + void MakeCubicTextureRef(TextureRef* tRef, plLayerInterface* layer, plCubicEnvironmap* img); ST::string GetErrorString() const { return fErrorMsg; } diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalDeviceRefs.cpp b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalDeviceRefs.cpp index 87d4f6181a..c493dfefb0 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalDeviceRefs.cpp +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalDeviceRefs.cpp @@ -45,9 +45,9 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plProfile.h" #include "plStatusLog/plStatusLog.h" -plProfile_CreateMemCounter("Vertices", "Memory", MemVertex); -plProfile_CreateMemCounter("Indices", "Memory", MemIndex); -plProfile_CreateMemCounter("Textures", "Memory", MemTexture); +plProfile_Extern(MemVertex); +plProfile_Extern(MemIndex); +plProfile_Extern(MemTexture); /***************************************************************************** ** Generic plGLDeviceRef Functions ** diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp index 951fd2590a..4cddac0438 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp @@ -86,64 +86,50 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com uint32_t fDbgSetupInitFlags; // HACK temp only -plProfile_CreateCounter("Feed Triangles", "Draw", DrawFeedTriangles); -plProfile_CreateCounter("Draw Prim Static", "Draw", DrawPrimStatic); -plProfile_CreateMemCounter("Total Texture Size", "Draw", TotalTexSize); -plProfile_CreateCounter("Layer Change", "Draw", LayChange); +plProfile_Extern(DrawFeedTriangles); +plProfile_Extern(DrawPrimStatic); +plProfile_Extern(TotalTexSize); +plProfile_Extern(LayChange); plProfile_Extern(DrawTriangles); plProfile_Extern(MatChange); - -plProfile_CreateTimer("PrepShadows", "PipeT", PrepShadows); -plProfile_CreateTimer("PrepDrawable", "PipeT", PrepDrawable); -plProfile_CreateTimer(" Skin", "PipeT", Skin); -plProfile_CreateTimer("RenderSpan", "PipeT", RenderSpan); -plProfile_CreateTimer(" MergeCheck", "PipeT", MergeCheck); -plProfile_CreateTimer(" MergeSpan", "PipeT", MergeSpan); -plProfile_CreateTimer(" SpanTransforms", "PipeT", SpanTransforms); -plProfile_CreateTimer(" SpanFog", "PipeT", SpanFog); -plProfile_CreateTimer(" SelectLights", "PipeT", SelectLights); -plProfile_CreateTimer(" SelectProj", "PipeT", SelectProj); -plProfile_CreateTimer(" CheckDyn", "PipeT", CheckDyn); -plProfile_CreateTimer(" CheckStat", "PipeT", CheckStat); -plProfile_CreateTimer(" RenderBuff", "PipeT", RenderBuff); -plProfile_CreateTimer(" RenderPrim", "PipeT", RenderPrim); -plProfile_CreateTimer("PlateMgr", "PipeT", PlateMgr); -plProfile_CreateTimer("DebugText", "PipeT", DebugText); -plProfile_CreateTimer("Reset", "PipeT", Reset); - -plProfile_CreateCounterNoReset("Reload", "PipeC", PipeReload); -plProfile_CreateCounter("AvRTPoolUsed", "PipeC", AvRTPoolUsed); -plProfile_CreateCounter("AvRTPoolCount", "PipeC", AvRTPoolCount); -plProfile_CreateCounter("AvRTPoolRes", "PipeC", AvRTPoolRes); -plProfile_CreateCounter("AvRTShrinkTime", "PipeC", AvRTShrinkTime); -plProfile_CreateCounter("NumSkin", "PipeC", NumSkin); +plProfile_Extern(NumSkin); + +plProfile_Extern(PipeReload); +plProfile_Extern(PrepShadows); +plProfile_Extern(PrepDrawable); +plProfile_Extern(Skin); +plProfile_Extern(RenderSpan); +plProfile_Extern(MergeCheck); +plProfile_Extern(MergeSpan); +plProfile_Extern(SpanTransforms); +plProfile_Extern(SpanFog); +plProfile_Extern(SelectLights); +plProfile_Extern(SelectProj); +plProfile_Extern(CheckDyn); +plProfile_Extern(CheckStat); +plProfile_Extern(RenderBuff); +plProfile_Extern(RenderPrim); +plProfile_Extern(PlateMgr); +plProfile_Extern(DebugText); +plProfile_Extern(Reset); +plProfile_Extern(AvRTPoolUsed); +plProfile_Extern(AvRTPoolCount); +plProfile_Extern(AvRTPoolRes); +plProfile_Extern(AvRTShrinkTime); plMetalEnumerate plMetalPipeline::enumerator; -class plRenderTriListFunc : public plRenderPrimFunc +class plMetalRenderTriListFunc : public plRenderTriListFunc { -protected: - plMetalDevice* fDevice; - int fBaseVertexIndex; - int fVStart; - int fVLength; - int fIStart; - int fNumTris; - public: - plRenderTriListFunc(plMetalDevice* device, int baseVertexIndex, + plMetalRenderTriListFunc(plMetalDevice* device, int baseVertexIndex, int vStart, int vLength, int iStart, int iNumTris) - : fDevice(device), - fBaseVertexIndex(baseVertexIndex), - fVStart(vStart), - fVLength(vLength), - fIStart(iStart), - fNumTris(iNumTris) {} + : plRenderTriListFunc(device, baseVertexIndex, vStart, vLength, iStart, iNumTris) {} bool RenderPrims() const override; }; -bool plRenderTriListFunc::RenderPrims() const +bool plMetalRenderTriListFunc::RenderPrims() const { plProfile_IncCount(DrawFeedTriangles, fNumTris); plProfile_IncCount(DrawTriangles, fNumTris); @@ -1183,7 +1169,7 @@ void plMetalPipeline::IRenderBufferSpan(const plIcicle& span, hsGDeviceRef* vb, /* Index Buffer stuff and drawing */ - plRenderTriListFunc render(&fDevice, 0, vStart, vLength, iStart, iLength / 3); + plMetalRenderTriListFunc render(&fDevice, 0, vStart, vLength, iStart, iLength / 3); plProfile_EndTiming(RenderBuff); @@ -1478,7 +1464,7 @@ void plMetalPipeline::IRenderAuxSpan(const plSpan& span, const plAuxSpan* aux) fState.fCurrentVertexBuffer = vRef->GetBuffer(); fDevice.fCurrentIndexBuffer = iRef->GetBuffer(); - plRenderTriListFunc render(&fDevice, 0, aux->fVStartIdx, aux->fVLength, aux->fIStartIdx, aux->fILength / 3); + plMetalRenderTriListFunc render(&fDevice, 0, aux->fVStartIdx, aux->fVLength, aux->fIStartIdx, aux->fILength / 3); for (int32_t pass = 0; pass < mRef->GetNumPasses(); pass++) { IHandleMaterialPass(material, pass, &span, vRef); @@ -2529,87 +2515,6 @@ void plMetalPipeline::PopCurrentLightSources() // Special effects ///////////////////////////////////////////////////////////// -// IPushOverBaseLayer ///////////////////////////////////////////////////////// -// Sets fOverBaseLayer (if any) as a wrapper on top of input layer. -// This allows the OverBaseLayer to intercept and modify queries of -// the real current layer's properties (e.g. color or state). -// fOverBaseLayer is set to only get applied to the base layer during -// multitexturing. -// Must be matched with call to IPopOverBaseLayer. -plLayerInterface* plMetalPipeline::IPushOverBaseLayer(plLayerInterface* li) -{ - if (!li) - return nullptr; - - fOverLayerStack.emplace_back(li); - - if (!fOverBaseLayer) - return fOverBaseLayer = li; - - fForceMatHandle = true; - fOverBaseLayer = fOverBaseLayer->Attach(li); - fOverBaseLayer->Eval(fTime, fFrame, 0); - return fOverBaseLayer; -} - -// IPopOverBaseLayer ///////////////////////////////////////////////////////// -// Removes fOverBaseLayer as wrapper on top of input layer. -// Should match calls to IPushOverBaseLayer. -plLayerInterface* plMetalPipeline::IPopOverBaseLayer(plLayerInterface* li) -{ - if (!li) - return nullptr; - - fForceMatHandle = true; - - plLayerInterface* pop = fOverLayerStack.back(); - fOverLayerStack.pop_back(); - fOverBaseLayer = fOverBaseLayer->Detach(pop); - - return pop; -} - -// IPushOverAllLayer /////////////////////////////////////////////////// -// Push fOverAllLayer (if any) as wrapper around the input layer. -// fOverAllLayer is set to be applied to each layer during multitexturing. -// Must be matched by call to IPopOverAllLayer -plLayerInterface* plMetalPipeline::IPushOverAllLayer(plLayerInterface* li) -{ - if (!li) - return nullptr; - - fOverLayerStack.push_back(li); - - if (!fOverAllLayer) { - fOverAllLayer = li; - fOverAllLayer->Eval(fTime, fFrame, 0); - return fOverAllLayer; - } - - fForceMatHandle = true; - fOverAllLayer = fOverAllLayer->Attach(li); - fOverAllLayer->Eval(fTime, fFrame, 0); - - return fOverAllLayer; -} - -// IPopOverAllLayer ////////////////////////////////////////////////// -// Remove fOverAllLayer as wrapper on top of input layer. -// Should match calls to IPushOverAllLayer. -plLayerInterface* plMetalPipeline::IPopOverAllLayer(plLayerInterface* li) -{ - if (!li) - return nullptr; - - fForceMatHandle = true; - - plLayerInterface* pop = fOverLayerStack.back(); - fOverLayerStack.pop_back(); - fOverAllLayer = fOverAllLayer->Detach(pop); - - return pop; -} - // IPushProjPiggyBack ////////////////////////////////////////////////// // Push a projected texture on as a piggy back. void plMetalPipeline::IPushProjPiggyBack(plLayerInterface* li) @@ -2897,18 +2802,6 @@ void plMetalPipeline::FindFragFunction() return pipe; }*/ -// IClearShadowSlaves /////////////////////////////////////////////////////////////////////////// -// At EndRender(), we need to clear our list of shadow slaves. They are only valid for one frame. -void plMetalPipeline::IClearShadowSlaves() -{ - int i; - for (i = 0; i < fShadows.size(); i++) { - const plShadowCaster* caster = fShadows[i]->fCaster; - caster->GetKey()->UnRefObject(); - } - fShadows.clear(); -} - // Create all our video memory consuming D3D objects. bool plMetalPipeline::ICreateDynDeviceObjects() { @@ -3710,7 +3603,7 @@ void plMetalPipeline::IRenderShadowCasterSpan(plShadowSlave* slave, plDrawableSp uint32_t iStart = span.fIPackedIdx; uint32_t iLength = span.fILength; - plRenderTriListFunc render(&fDevice, 0, vStart, vLength, iStart, iLength / 3); + plMetalRenderTriListFunc render(&fDevice, 0, vStart, vLength, iStart, iLength / 3); static hsMatrix44 emptyMatrix; hsMatrix44 m = emptyMatrix; @@ -3956,26 +3849,13 @@ void plMetalPipeline::ISetupShadowSlaveTextures(plShadowSlave* slave) fCurrentRenderPassUniforms->uvTransforms[1].transform = tXfm; } -/////////////////////////////////////////////////////////////////////////////// -//// View Stuff /////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// - -//// IIsViewLeftHanded //////////////////////////////////////////////////////// -// Returns true if the combination of the local2world and world2camera -// matrices is left-handed. - -bool plMetalPipeline::IIsViewLeftHanded() -{ - return fView.GetViewTransform().GetOrthogonal() ^ (fView.fLocalToWorldLeftHanded ^ fView.fWorldToCamLeftHanded) ? true : false; -} - //// ISetCullMode ///////////////////////////////////////////////////////////// // Tests and sets the current winding order cull mode (CW, CCW, or none). // Will reverse the cull mode as necessary for left handed camera or local to world // transforms. void plMetalPipeline::ISetCullMode(bool flip) { - MTL::CullMode newCullMode = !IIsViewLeftHanded() ^ !flip ? MTL::CullModeFront : MTL::CullModeBack; + MTL::CullMode newCullMode = !fView.IsViewLeftHanded() ^ !flip ? MTL::CullModeFront : MTL::CullModeBack; if (fState.fCurrentCullMode != newCullMode) { fDevice.CurrentRenderCommandEncoder()->setCullMode(newCullMode); fState.fCurrentCullMode = newCullMode; @@ -4219,159 +4099,21 @@ void plMetalPipeline::IBlendVertBuffer(plSpan* span, hsMatrix44* matrixPalette, } } -// Resource checking - -// CheckTextureRef ////////////////////////////////////////////////////// -// Make sure the given layer's texture has background D3D resources allocated. -void plMetalPipeline::CheckTextureRef(plLayerInterface* layer) -{ - plBitmap* bitmap = layer->GetTexture(); - - if (bitmap) { - CheckTextureRef(bitmap); - } -} - -void plMetalPipeline::CheckTextureRef(plBitmap* bitmap) -{ - plMetalTextureRef* tRef = static_cast(bitmap->GetDeviceRef()); - - if (!tRef) { - tRef = static_cast(MakeTextureRef(bitmap)); - } - - // If it's dirty, refill it. - if (tRef->IsDirty()) { - IReloadTexture(bitmap, tRef); - } -} - -hsGDeviceRef* plMetalPipeline::MakeTextureRef(plBitmap* bitmap) -{ - plMetalTextureRef* tRef = static_cast(bitmap->GetDeviceRef()); - - if (!tRef) { - tRef = new plMetalTextureRef(); - - fDevice.SetupTextureRef(bitmap, tRef); - } - - if (!tRef->IsLinked()) { - tRef->Link(&fTextureRefList); - } - - // Make sure it has all resources created. - fDevice.CheckTexture(tRef); - - // If it's dirty, refill it. - if (tRef->IsDirty()) { - IReloadTexture(bitmap, tRef); - } - return tRef; -} - void plMetalPipeline::IReloadTexture(plBitmap* bitmap, plMetalTextureRef* ref) { plMipmap* mip = plMipmap::ConvertNoRef(bitmap); if (mip) { - fDevice.MakeTextureRef(ref, mip); + fDevice.MakeTextureRef(ref, nullptr, mip); return; } plCubicEnvironmap* cubic = plCubicEnvironmap::ConvertNoRef(bitmap); if (cubic) { - fDevice.MakeCubicTextureRef(ref, cubic); + fDevice.MakeCubicTextureRef(ref, nullptr, cubic); return; } } -// CheckVertexBufferRef ///////////////////////////////////////////////////// -// Make sure the buffer group has a valid buffer ref and that it is up to date. -void plMetalPipeline::CheckVertexBufferRef(plGBufferGroup* owner, uint32_t idx) -{ - // First, do we have a device ref at this index? - plMetalVertexBufferRef* vRef = static_cast(owner->GetVertexBufferRef(idx)); - - // If not - if (!vRef) { - // Make the blank ref - vRef = new plMetalVertexBufferRef(); - - fDevice.SetupVertexBufferRef(owner, idx, vRef); - } - - if (!vRef->IsLinked()) { - vRef->Link(&fVtxBuffRefList); - } - - // One way or another, we now have a vbufferref[idx] in owner. - // Now, does it need to be (re)filled? - // If the owner is volatile, then we hold off. It might not - // be visible, and we might need to refill it again if we - // have an overrun of our dynamic buffer. - if (!vRef->Volatile()) { - // If it's a static buffer, allocate a vertex buffer for it. - fDevice.CheckStaticVertexBuffer(vRef, owner, idx); - - // Might want to remove this assert, and replace it with a dirty check - // if we have static buffers that change very seldom rather than never. - hsAssert(!vRef->IsDirty(), "Non-volatile vertex buffers should never get dirty"); - } else { - // Make sure we're going to be ready to fill it. - if (!vRef->fData && (vRef->fFormat != owner->GetVertexFormat())) { - vRef->fData = new uint8_t[vRef->fCount * vRef->fVertexSize]; - fDevice.FillVolatileVertexBufferRef(vRef, owner, idx); - } - } -} - -// CheckIndexBufferRef ///////////////////////////////////////////////////// -// Make sure the buffer group has an index buffer ref and that its data is current. -void plMetalPipeline::CheckIndexBufferRef(plGBufferGroup* owner, uint32_t idx) -{ - plMetalIndexBufferRef* iRef = static_cast(owner->GetIndexBufferRef(idx)); - - if (!iRef) { - // Create one from scratch. - iRef = new plMetalIndexBufferRef(); - - fDevice.SetupIndexBufferRef(owner, idx, iRef); - } - - if (!iRef->IsLinked()) { - iRef->Link(&fIdxBuffRefList); - } - - // Make sure it has all resources created. - fDevice.CheckIndexBuffer(iRef); - - // If it's dirty, refill it. - if (iRef->IsDirty()) { - fDevice.FillIndexBufferRef(iRef, owner, idx); - } -} - -//// IGetBufferFormatSize ///////////////////////////////////////////////////// -// Calculate the vertex stride from the given format. -uint32_t plMetalPipeline::IGetBufferFormatSize(uint8_t format) const -{ - uint32_t size = sizeof(float) * 6 + sizeof(uint32_t) * 2; // Position and normal, and two packed colors - - switch (format & plGBufferGroup::kSkinWeightMask) { - case plGBufferGroup::kSkinNoWeights: - break; - case plGBufferGroup::kSkin1Weight: - size += sizeof(float); - break; - default: - hsAssert(false, "Invalid skin weight value in IGetBufferFormatSize()"); - } - - size += sizeof(float) * 3 * plGBufferGroup::CalcNumUVs(format); - - return size; -} - void plMetalPipeline::plMetalPipelineCurrentState::Reset() { fCurrentPipelineState = nullptr; diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h index 4d819490c7..c150b19b73 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h @@ -73,17 +73,6 @@ class plMetalEnumerate static void Enumerate(std::vector& records); }; -//// Helper Classes /////////////////////////////////////////////////////////// - -//// The RenderPrimFunc lets you have one function which does a lot of stuff -// around the actual call to render whatever type of primitives you have, instead -// of duplicating everything because the one line to render is different. -class plRenderPrimFunc -{ -public: - virtual bool RenderPrims() const = 0; // return true on error -}; - class plMetalPipeline : public pl3DPipeline { public: @@ -94,7 +83,7 @@ class plMetalPipeline : public pl3DPipeline friend class plMetalDevice; friend class plMetalPlateManager; friend class plMetalMaterialShaderRef; - friend class plRenderTriListFunc; + friend class plMetalRenderTriListFunc; friend class plMetalTextFont; plMetalMaterialShaderRef* fMatRefList; @@ -151,16 +140,8 @@ class plMetalPipeline : public pl3DPipeline bool IHandleMaterialPass(hsGMaterial* material, uint32_t pass, const plSpan* currSpan, const plMetalVertexBufferRef* vRef, const bool allowShaders = true); plMetalDevice* GetMetalDevice() const; - // Create and/or Refresh geometry buffers - void CheckVertexBufferRef(plGBufferGroup* owner, uint32_t idx) override; - void CheckIndexBufferRef(plGBufferGroup* owner, uint32_t idx) override; - void CheckTextureRef(plLayerInterface* lay) override; - void CheckTextureRef(plBitmap* bitmap); - hsGDeviceRef* MakeTextureRef(plBitmap* bitmap); void IReloadTexture(plBitmap* bitmap, plMetalTextureRef* ref); - uint32_t IGetBufferFormatSize(uint8_t format) const; - plRenderTarget* PopRenderTarget() override; MTL::PixelFormat GetFramebufferFormat() { return fDevice.GetFramebufferFormat(); }; @@ -185,7 +166,6 @@ class plMetalPipeline : public pl3DPipeline void IPreprocessAvatarTextures(); void IDrawClothingQuad(float x, float y, float w, float h, float uOff, float vOff, plMipmap* tex); - void IClearShadowSlaves(); void ICreateDeviceObjects(); void IReleaseDynDeviceObjects(); @@ -193,14 +173,8 @@ class plMetalPipeline : public pl3DPipeline void IReleaseDynamicBuffers(); void IReleaseDeviceObjects(); - bool IIsViewLeftHanded(); void ISetCullMode(bool flip = false); - plLayerInterface* IPushOverBaseLayer(plLayerInterface* li); - plLayerInterface* IPopOverBaseLayer(plLayerInterface* li); - plLayerInterface* IPushOverAllLayer(plLayerInterface* li); - plLayerInterface* IPopOverAllLayer(plLayerInterface* li); - void IPushPiggyBacks(hsGMaterial* mat); void IPopPiggyBacks(); void IPushProjPiggyBack(plLayerInterface* li); @@ -267,8 +241,6 @@ class plMetalPipeline : public pl3DPipeline static plMetalEnumerate enumerator; - plTextFont* fTextFontRefList; - NS::AutoreleasePool* fCurrentPool; /// Describes the state for the "fixed function" shader. diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h index e5599bd0f5..b4bfc98980 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h @@ -65,6 +65,7 @@ class plSharedMesh; class plStateDataRecord; class plDXPipeline; class plMetalPipeline; +class plGLPipeline; struct plClothingItemOptions { @@ -165,6 +166,7 @@ class plClothingOutfit : public plSynchedObject { friend class plDXPipeline; friend class plMetalPipeline; + friend class plGLPipeline; public: plArmatureMod *fAvatar; diff --git a/Sources/Plasma/PubUtilLib/plPipeline/pl3DPipeline.cpp b/Sources/Plasma/PubUtilLib/plPipeline/pl3DPipeline.cpp index 00ba9ca637..dc7372b41e 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/pl3DPipeline.cpp +++ b/Sources/Plasma/PubUtilLib/plPipeline/pl3DPipeline.cpp @@ -45,6 +45,26 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com plProfile_CreateTimer("RenderScene", "PipeT", RenderScene); plProfile_CreateTimer("VisEval", "PipeT", VisEval); plProfile_CreateTimer("VisSelect", "PipeT", VisSelect); +plProfile_CreateTimer("PlateMgr", "PipeT", PlateMgr); +plProfile_CreateTimer("DebugText", "PipeT", DebugText); +plProfile_CreateTimer("Reset", "PipeT", Reset); +plProfile_CreateTimer("PrepShadows", "PipeT", PrepShadows); +plProfile_CreateTimer("PrepDrawable", "PipeT", PrepDrawable); +plProfile_CreateTimer(" Skin", "PipeT", Skin); +plProfile_CreateTimer(" AvSort", "PipeT", AvatarSort); +plProfile_CreateTimer(" ClearLights", "PipeT", ClearLights); + +plProfile_CreateTimer("RenderSpan", "PipeT", RenderSpan); +plProfile_CreateTimer(" MergeCheck", "PipeT", MergeCheck); +plProfile_CreateTimer(" MergeSpan", "PipeT", MergeSpan); +plProfile_CreateTimer(" SpanTransforms", "PipeT", SpanTransforms); +plProfile_CreateTimer(" SpanFog", "PipeT", SpanFog); +plProfile_CreateTimer(" SelectLights", "PipeT", SelectLights); +plProfile_CreateTimer(" SelectProj", "PipeT", SelectProj); +plProfile_CreateTimer(" CheckDyn", "PipeT", CheckDyn); +plProfile_CreateTimer(" CheckStat", "PipeT", CheckStat); +plProfile_CreateTimer(" RenderBuff", "PipeT", RenderBuff); +plProfile_CreateTimer(" RenderPrim", "PipeT", RenderPrim); plProfile_CreateTimer("FindSceneLights", "PipeT", FindSceneLights); plProfile_CreateTimer(" Find Lights", "PipeT", FindLights); @@ -62,10 +82,28 @@ plProfile_CreateCounter("LightChar", "PipeC", LightChar); plProfile_CreateCounter("LightActive", "PipeC", LightActive); plProfile_CreateCounter("Lights Found", "PipeC", FindLightsFound); plProfile_CreateCounter("Perms Found", "PipeC", FindLightsPerm); +plProfile_CreateCounter("NumSkin", "PipeC", NumSkin); +plProfile_CreateCounter("AvRTPoolUsed", "PipeC", AvRTPoolUsed); +plProfile_CreateCounter("AvRTPoolCount", "PipeC", AvRTPoolCount); +plProfile_CreateCounter("AvRTPoolRes", "PipeC", AvRTPoolRes); +plProfile_CreateCounter("AvRTShrinkTime", "PipeC", AvRTShrinkTime); +plProfile_CreateCounter("AvatarFaces", "PipeC", AvatarFaces); +plProfile_CreateCounter("Merge", "PipeC", SpanMerge); +plProfile_CreateCounter("LiState", "PipeC", MatLightState); +plProfile_CreateCounter("EmptyList", "PipeC", EmptyList); + +plProfile_CreateCounterNoReset("Reload", "PipeC", PipeReload); plProfile_CreateCounter("Polys", "General", DrawTriangles); plProfile_CreateCounter("Material Change", "Draw", MatChange); - +plProfile_CreateCounter("Feed Triangles", "Draw", DrawFeedTriangles); +plProfile_CreateCounter("Draw Prim Static", "Draw", DrawPrimStatic); +plProfile_CreateCounter("Layer Change", "Draw", LayChange); + +plProfile_CreateMemCounter("Total Texture Size", "Draw", TotalTexSize); +plProfile_CreateMemCounter("Vertices", "Memory", MemVertex); +plProfile_CreateMemCounter("Indices", "Memory", MemIndex); +plProfile_CreateMemCounter("Textures", "Memory", MemTexture); PipelineParams plPipeline::fDefaultPipeParams; PipelineParams plPipeline::fInitialPipeParams; diff --git a/Sources/Plasma/PubUtilLib/plPipeline/pl3DPipeline.h b/Sources/Plasma/PubUtilLib/plPipeline/pl3DPipeline.h index 5827a1feb9..f5931a4210 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/pl3DPipeline.h +++ b/Sources/Plasma/PubUtilLib/plPipeline/pl3DPipeline.h @@ -100,6 +100,8 @@ plProfile_Extern(LightChar); plProfile_Extern(LightActive); plProfile_Extern(FindLightsFound); plProfile_Extern(FindLightsPerm); +plProfile_Extern(AvatarSort); +plProfile_Extern(AvatarFaces); static const float kPerspLayerScale = 0.00001f; static const float kPerspLayerScaleW = 0.001f; @@ -107,7 +109,62 @@ static const float kPerspLayerTrans = 0.00002f; static const float kAvTexPoolShrinkThresh = 30.f; // seconds -template +//// Helper Classes /////////////////////////////////////////////////////////// + +/** + * The RenderPrimFunc lets you have one function which does a lot of stuff + * around the actual call to render whatever type of primitives you have, + * instead of duplicating everything because the one line to render is + * different. + * + * These allow the same setup code path to be followed, no matter what the + * primitive type (i.e. data-type/draw-call is going to happen once the render + * state is set. + * Originally useful to make one code path for trilists, tri-patches, and + * rect-patches, but we've since dropped support for patches. We still use the + * RenderNil function to allow the code to go through all the state setup + * without knowing whether a render call is going to come out the other end. + * + * Would allow easy extension for supporting tristrips or pointsprites, but + * we've never had a strong reason to use either. + */ +class plRenderPrimFunc +{ +public: + virtual bool RenderPrims() const = 0; // return true on error +}; + + +class plRenderNilFunc : public plRenderPrimFunc +{ +public: + plRenderNilFunc() {} + + virtual bool RenderPrims() const { return false; } +}; + + +template +class plRenderTriListFunc : public plRenderPrimFunc +{ +protected: + DeviceType* fDevice; + int fBaseVertexIndex; + int fVStart; + int fVLength; + int fIStart; + int fNumTris; + +public: + plRenderTriListFunc(DeviceType* device, int baseVertexIndex, int vStart, int vLength, int iStart, int iNumTris) + : fDevice(device), fBaseVertexIndex(baseVertexIndex), fVStart(vStart), + fVLength(vLength), fIStart(iStart), fNumTris(iNumTris) {} +}; + + +//// Class Definition ///////////////////////////////////////////////////////// + +template class pl3DPipeline : public plPipeline { protected: @@ -142,6 +199,7 @@ class pl3DPipeline : public plPipeline typename DeviceType::VertexBufferRef* fVtxBuffRefList; typename DeviceType::IndexBufferRef* fIdxBuffRefList; typename DeviceType::TextureRef* fTextureRefList; + plTextFont* fTextFontRefList; hsGDeviceRef* fLayerRef[8]; @@ -242,11 +300,25 @@ class pl3DPipeline : public plPipeline //virtual plTextFont* MakeTextFont(ST::string face, uint16_t size) = 0; - //virtual void CheckVertexBufferRef(plGBufferGroup* owner, uint32_t idx) = 0; - //virtual void CheckIndexBufferRef(plGBufferGroup* owner, uint32_t idx) = 0; + + /** + * Make sure the buffer group has a vertex buffer ref and that its data is + * current. + */ + void CheckVertexBufferRef(plGBufferGroup* owner, uint32_t idx) override; + + /** + * Make sure the buffer group has an index buffer ref and that its data is + * current. + */ + void CheckIndexBufferRef(plGBufferGroup* owner, uint32_t idx) override; + //virtual bool OpenAccess(plAccessSpan& dst, plDrawableSpans* d, const plVertexSpan* span, bool readOnly) = 0; //virtual bool CloseAccess(plAccessSpan& acc) = 0; - //virtual void CheckTextureRef(plLayerInterface* lay) = 0; + + void CheckTextureRef(plLayerInterface* lay) override; + + void CheckTextureRef(plBitmap* bmp); void SetDefaultFogEnviron(plFogEnvironment* fog) override { fView.SetDefaultFog(*fog); @@ -614,7 +686,6 @@ class pl3DPipeline : public plPipeline /** Removes a layer wrapper installed by AppendLayerInterface. */ plLayerInterface* RemoveLayerInterface(plLayerInterface* li, bool onAllLayers = false) override; - /** * Return the current bits set to be always on for the given category * (e.g. ZFlags). @@ -768,6 +839,12 @@ class pl3DPipeline : public plPipeline */ void ISetShadowFromGroup(plDrawableSpans* drawable, const plSpan* span, plLightInfo* liInfo); + /** + * At EndRender(), we need to clear our list of shadow slaves. + * They are only valid for one frame. + */ + void IClearShadowSlaves(); + void IClearClothingOutfits(std::vector* outfits); void IFillAvRTPool(); @@ -780,6 +857,39 @@ class pl3DPipeline : public plPipeline plRenderTarget* IGetNextAvRT(); void IFreeAvRT(plRenderTarget* tex); + /** + * Sets fOverBaseLayer (if any) as a wrapper on top of input layer. + * This allows the OverBaseLayer to intercept and modify queries of + * the real current layer's properties (e.g. color or state). + * fOverBaseLayer is set to only get applied to the base layer during + * multitexturing. + * + * Must be matched with call to IPopOverBaseLayer. + */ + plLayerInterface* IPushOverBaseLayer(plLayerInterface* li); + + /** + * Removes fOverBaseLayer as wrapper on top of input layer. + * + * Should match calls to IPushOverBaseLayer. + */ + plLayerInterface* IPopOverBaseLayer(plLayerInterface* li); + + /** + * Push fOverAllLayer (if any) as wrapper around the input layer. + * + * fOverAllLayer is set to be applied to each layer during multitexturing. + * + * Must be matched by call to IPopOverAllLayer + */ + plLayerInterface* IPushOverAllLayer(plLayerInterface* li); + + /** + * Remove fOverAllLayer as wrapper on top of input layer. + * + * Should match calls to IPushOverAllLayer. + */ + plLayerInterface* IPopOverAllLayer(plLayerInterface* li); /** * For every span in the list of visible span indices, find the list of @@ -835,10 +945,36 @@ class pl3DPipeline : public plPipeline /** pass the current local to world tranform on to the device. */ void ILocalToWorldToDevice(); + + /** + * Sorts the avatar geometry for display. + * + * We handle avatar sort differently from the rest of the face sort. The + * reason is that within the single avatar index buffer, we want to only + * sort the faces of spans requesting a sort, and sort them in place. + * + * Contrast that with the normal scene translucency sort. There, we sort + * all the spans in a drawble, then we sort all the faces in that drawable, + * then for each span in the sorted span list, we extract the faces for + * that span appending onto the index buffer. This gives great efficiency + * because only the visible faces are sorted and they wind up packed into + * the front of the index buffer, which permits more batching. See + * plDrawableSpans::SortVisibleSpans. + * + * For the avatar, it's generally the case that all the avatar is visible + * or not, and there is only one material, so neither of those efficiencies + * is helpful. Moreover, for the avatar the faces we want sorted are a tiny + * subset of the avatar's faces. Moreover, and most importantly, for the + * avatar, we want to preserve the order that spans are drawn, so, for + * example, the opaque base head will always be drawn before the + * translucent hair fringe, which will always be drawn before the pink + * clear plastic baseball cap. + */ + bool IAvatarSort(plDrawableSpans* d, const std::vector& visList); }; -template +template pl3DPipeline::pl3DPipeline(const hsG3DDeviceModeRecord* devModeRec) : fMaxLayersAtOnce(-1), fMaxPiggyBacks(), @@ -850,6 +986,8 @@ pl3DPipeline::pl3DPipeline(const hsG3DDeviceModeRecord* devModeRec) fActivePiggyBacks(), fVtxBuffRefList(), fIdxBuffRefList(), + fTextureRefList(), + fTextFontRefList(), fCurrMaterial(), fCurrLay(), fCurrNumLayers(), @@ -906,7 +1044,7 @@ pl3DPipeline::pl3DPipeline(const hsG3DDeviceModeRecord* devModeRec) fVSync = fInitialPipeParams.VSync; } -template +template pl3DPipeline::~pl3DPipeline() { fCurrLay = nullptr; @@ -921,12 +1059,31 @@ pl3DPipeline::~pl3DPipeline() while (fActiveLights) UnRegisterLight(fActiveLights); + while (fVtxBuffRefList) { + typename DeviceType::VertexBufferRef* ref = fVtxBuffRefList; + ref->Release(); + ref->Unlink(); + } + + while (fIdxBuffRefList) { + typename DeviceType::IndexBufferRef* ref = fIdxBuffRefList; + ref->Release(); + ref->Unlink(); + } + + while (fTextureRefList) { + typename DeviceType::TextureRef* ref = fTextureRefList; + ref->Release(); + ref->Unlink(); + } + + IReleaseAvRTPool(); IClearClothingOutfits(&fClothingOutfits); IClearClothingOutfits(&fPrevClothingOutfits); } -template +template void pl3DPipeline::Render(plDrawable* d, const std::vector& visList) { // Reset here, since we can push/pop renderTargets after BeginRender() but @@ -941,7 +1098,7 @@ void pl3DPipeline::Render(plDrawable* d, const std::vector& } -template +template void pl3DPipeline::Draw(plDrawable* d) { plDrawableSpans *ds = plDrawableSpans::ConvertNoRef(d); @@ -959,7 +1116,138 @@ void pl3DPipeline::Draw(plDrawable* d) } -template +template +void pl3DPipeline::CheckVertexBufferRef(plGBufferGroup* owner, uint32_t idx) +{ + // First, do we have a device ref at this index? + typename DeviceType::VertexBufferRef* vRef = static_cast(owner->GetVertexBufferRef(idx)); + + // If not + if (!vRef) { + // Make the blank ref + vRef = new typename DeviceType::VertexBufferRef(); + fDevice.SetupVertexBufferRef(owner, idx, vRef); + } + + if (!vRef->IsLinked()) + vRef->Link(&fVtxBuffRefList); + + // One way or another, we now have a vbufferref[idx] in owner. + // Now, does it need to be (re)filled? + // If the owner is volatile, then we hold off. It might not + // be visible, and we might need to refill it again if we + // have an overrun of our dynamic buffer. + if (!vRef->Volatile()) { + // If it's a static buffer, allocate a vertex buffer for it. + fDevice.CheckStaticVertexBuffer(vRef, owner, idx); + + // Might want to remove this assert, and replace it with a dirty check + // if we have static buffers that change very seldom rather than never. + hsAssert(!vRef->IsDirty(), "Non-volatile vertex buffers should never get dirty"); + } + else + { + // Make sure we're going to be ready to fill it. + if (!vRef->fData && (vRef->fFormat != owner->GetVertexFormat())) { + vRef->fData = new uint8_t[vRef->fCount * vRef->fVertexSize]; + fDevice.FillVolatileVertexBufferRef(vRef, owner, idx); + } + } +} + + +template +void pl3DPipeline::CheckIndexBufferRef(plGBufferGroup* owner, uint32_t idx) +{ + typename DeviceType::IndexBufferRef* iRef = static_cast(owner->GetIndexBufferRef(idx)); + + if (!iRef) { + // Create one from scratch. + iRef = new typename DeviceType::IndexBufferRef(); + fDevice.SetupIndexBufferRef(owner, idx, iRef); + } + + if (!iRef->IsLinked()) + iRef->Link(&fIdxBuffRefList); + + // Make sure it has all resources created. + fDevice.CheckIndexBuffer(iRef); + + // If it's dirty, refill it. + if (iRef->IsDirty()) + fDevice.FillIndexBufferRef(iRef, owner, idx); +} + +template +void pl3DPipeline::CheckTextureRef(plLayerInterface* layer) +{ + plBitmap* bitmap = layer->GetTexture(); + + if (bitmap) { + typename DeviceType::TextureRef* tRef = static_cast(bitmap->GetDeviceRef()); + + if (!tRef) { + tRef = new typename DeviceType::TextureRef(); + fDevice.SetupTextureRef(layer, bitmap, tRef); + } + + if (!tRef->IsLinked()) + tRef->Link(&fTextureRefList); + + // Make sure it has all resources created. + fDevice.CheckTexture(tRef); + + // If it's dirty, refill it. + if (tRef->IsDirty()) { + plMipmap* mip = plMipmap::ConvertNoRef(bitmap); + if (mip) { + fDevice.MakeTextureRef(tRef, layer, mip); + return; + } + + plCubicEnvironmap* cubic = plCubicEnvironmap::ConvertNoRef(bitmap); + if (cubic) { + fDevice.MakeCubicTextureRef(tRef, layer, cubic); + return; + } + } + } +} + +template +void pl3DPipeline::CheckTextureRef(plBitmap* bitmap) +{ + typename DeviceType::TextureRef* tRef = static_cast(bitmap->GetDeviceRef()); + + if (!tRef) { + tRef = new typename DeviceType::TextureRef(); + fDevice.SetupTextureRef(nullptr, bitmap, tRef); + } + + if (!tRef->IsLinked()) + tRef->Link(&fTextureRefList); + + // Make sure it has all resources created. + fDevice.CheckTexture(tRef); + + // If it's dirty, refill it. + if (tRef->IsDirty()) { + plMipmap* mip = plMipmap::ConvertNoRef(bitmap); + if (mip) { + fDevice.MakeTextureRef(tRef, nullptr, mip); + return; + } + + plCubicEnvironmap* cubic = plCubicEnvironmap::ConvertNoRef(bitmap); + if (cubic) { + fDevice.MakeCubicTextureRef(tRef, nullptr, cubic); + return; + } + } +} + + +template void pl3DPipeline::RegisterLight(plLightInfo* liInfo) { if (liInfo->IsLinked()) @@ -971,7 +1259,7 @@ void pl3DPipeline::RegisterLight(plLightInfo* liInfo) } -template +template void pl3DPipeline::UnRegisterLight(plLightInfo* liInfo) { liInfo->SetDeviceRef(nullptr); @@ -979,7 +1267,7 @@ void pl3DPipeline::UnRegisterLight(plLightInfo* liInfo) } -template +template void pl3DPipeline::PushRenderTarget(plRenderTarget* target) { fCurrRenderTarget = target; @@ -995,7 +1283,7 @@ void pl3DPipeline::PushRenderTarget(plRenderTarget* target) } -template +template plRenderTarget* pl3DPipeline::PopRenderTarget() { plRenderTarget* old = fRenderTargets.back(); @@ -1026,7 +1314,7 @@ plRenderTarget* pl3DPipeline::PopRenderTarget() } -template +template void pl3DPipeline::BeginVisMgr(plVisMgr* visMgr) { // Make Light Lists ///////////////////////////////////////////////////// @@ -1090,7 +1378,7 @@ void pl3DPipeline::BeginVisMgr(plVisMgr* visMgr) } -template +template void pl3DPipeline::EndVisMgr(plVisMgr* visMgr) { fCharLights.clear(); @@ -1098,7 +1386,7 @@ void pl3DPipeline::EndVisMgr(plVisMgr* visMgr) } -template +template bool pl3DPipeline::CheckResources() { if ((fClothingOutfits.size() <= 1 && fAvRTPool.size() > 1) || @@ -1112,7 +1400,7 @@ bool pl3DPipeline::CheckResources() } -template +template void pl3DPipeline::SetZBiasScale(float scale) { scale += 1.0f; @@ -1121,14 +1409,14 @@ void pl3DPipeline::SetZBiasScale(float scale) } -template +template float pl3DPipeline::GetZBiasScale() const { return (fTweaks.fPerspLayerScale / fTweaks.fDefaultPerspLayerScale) - 1.0f; } -template +template void pl3DPipeline::SetWorldToCamera(const hsMatrix44& w2c, const hsMatrix44& c2w) { plViewTransform& view_xform = fView.GetViewTransform(); @@ -1142,7 +1430,7 @@ void pl3DPipeline::SetWorldToCamera(const hsMatrix44& w2c, const hsM } -template +template void pl3DPipeline::ScreenToWorldPoint(int n, uint32_t stride, int32_t* scrX, int32_t* scrY, float dist, uint32_t strideOut, hsPoint3* worldOut) { while (n--) { @@ -1153,7 +1441,7 @@ void pl3DPipeline::ScreenToWorldPoint(int n, uint32_t stride, int32_ } -template +template void pl3DPipeline::RefreshScreenMatrices() { fView.fCullTreeDirty = true; @@ -1161,7 +1449,7 @@ void pl3DPipeline::RefreshScreenMatrices() } -template +template hsGMaterial* pl3DPipeline::PushOverrideMaterial(hsGMaterial* mat) { hsGMaterial* ret = GetOverrideMaterial(); @@ -1173,7 +1461,7 @@ hsGMaterial* pl3DPipeline::PushOverrideMaterial(hsGMaterial* mat) } -template +template void pl3DPipeline::PopOverrideMaterial(hsGMaterial* restore) { hsGMaterial *pop = fOverrideMat.back(); @@ -1185,7 +1473,7 @@ void pl3DPipeline::PopOverrideMaterial(hsGMaterial* restore) } -template +template plLayerInterface* pl3DPipeline::AppendLayerInterface(plLayerInterface* li, bool onAllLayers) { fForceMatHandle = true; @@ -1196,7 +1484,7 @@ plLayerInterface* pl3DPipeline::AppendLayerInterface(plLayerInterfac } -template +template plLayerInterface* pl3DPipeline::RemoveLayerInterface(plLayerInterface* li, bool onAllLayers) { fForceMatHandle = true; @@ -1214,7 +1502,7 @@ plLayerInterface* pl3DPipeline::RemoveLayerInterface(plLayerInterfac } -template +template hsGMatState pl3DPipeline::PushMaterialOverride(const hsGMatState& state, bool on) { hsGMatState ret = GetMaterialOverride(on); @@ -1230,7 +1518,7 @@ hsGMatState pl3DPipeline::PushMaterialOverride(const hsGMatState& st } -template +template hsGMatState pl3DPipeline::PushMaterialOverride(hsGMatState::StateIdx cat, uint32_t which, bool on) { hsGMatState ret = GetMaterialOverride(on); @@ -1246,7 +1534,7 @@ hsGMatState pl3DPipeline::PushMaterialOverride(hsGMatState::StateIdx } -template +template void pl3DPipeline::PopMaterialOverride(const hsGMatState& restore, bool on) { if (on) { @@ -1260,7 +1548,7 @@ void pl3DPipeline::PopMaterialOverride(const hsGMatState& restore, b } -template +template void pl3DPipeline::SubmitShadowSlave(plShadowSlave* slave) { // Check that it's a valid slave. @@ -1286,7 +1574,7 @@ void pl3DPipeline::SubmitShadowSlave(plShadowSlave* slave) } -template +template void pl3DPipeline::SubmitClothingOutfit(plClothingOutfit* co) { auto iter = std::find(fClothingOutfits.cbegin(), fClothingOutfits.cend(), co); @@ -1302,7 +1590,7 @@ void pl3DPipeline::SubmitClothingOutfit(plClothingOutfit* co) } -template +template plLayerInterface* pl3DPipeline::PushPiggyBackLayer(plLayerInterface* li) { fPiggyBackStack.push_back(li); @@ -1315,7 +1603,7 @@ plLayerInterface* pl3DPipeline::PushPiggyBackLayer(plLayerInterface* } -template +template plLayerInterface* pl3DPipeline::PopPiggyBackLayer(plLayerInterface* li) { auto iter = std::find(fPiggyBackStack.cbegin(), fPiggyBackStack.cend(), li); @@ -1332,7 +1620,7 @@ plLayerInterface* pl3DPipeline::PopPiggyBackLayer(plLayerInterface* } -template +template void pl3DPipeline::SetViewTransform(const plViewTransform& v) { fView.SetViewTransform(v); @@ -1351,7 +1639,7 @@ void pl3DPipeline::SetViewTransform(const plViewTransform& v) /*** PROTECTED METHODS *******************************************************/ -template +template void pl3DPipeline::IAttachSlaveToReceivers(size_t which, plDrawableSpans* drawable, const std::vector& visList) { plShadowSlave* slave = fShadows[which]; @@ -1399,7 +1687,7 @@ void pl3DPipeline::IAttachSlaveToReceivers(size_t which, plDrawableS } -template +template void pl3DPipeline::IAttachShadowsToReceivers(plDrawableSpans* drawable, const std::vector& visList) { for (size_t i = 0; i < fShadows.size(); i++) @@ -1407,7 +1695,7 @@ void pl3DPipeline::IAttachShadowsToReceivers(plDrawableSpans* drawab } -template +template bool pl3DPipeline::IAcceptsShadow(const plSpan* span, plShadowSlave* slave) { // The span's shadow bits records which shadow maps that span was rendered @@ -1416,7 +1704,7 @@ bool pl3DPipeline::IAcceptsShadow(const plSpan* span, plShadowSlave* } -template +template bool pl3DPipeline::IReceivesShadows(const plSpan* span, hsGMaterial* mat) { if (span->fProps & plSpan::kPropNoShadow) @@ -1437,7 +1725,7 @@ bool pl3DPipeline::IReceivesShadows(const plSpan* span, hsGMaterial* } -template +template void pl3DPipeline::ISetShadowFromGroup(plDrawableSpans* drawable, const plSpan* span, plLightInfo* liInfo) { hsGMaterial* mat = drawable->GetMaterial(span->fMaterialIdx); @@ -1461,8 +1749,19 @@ void pl3DPipeline::ISetShadowFromGroup(plDrawableSpans* drawable, co } +template +void pl3DPipeline::IClearShadowSlaves() +{ + for (plShadowSlave* shadow : fShadows) + { + const plShadowCaster* caster = shadow->fCaster; + caster->GetKey()->UnRefObject(); + } + fShadows.clear(); +} + -template +template void pl3DPipeline::IClearClothingOutfits(std::vector* outfits) { while (!outfits->empty()) { @@ -1475,7 +1774,7 @@ void pl3DPipeline::IClearClothingOutfits(std::vector +template void pl3DPipeline::IFillAvRTPool() { fAvNextFreeRT = 0; @@ -1501,7 +1800,7 @@ void pl3DPipeline::IFillAvRTPool() } -template +template bool pl3DPipeline::IFillAvRTPool(uint16_t numRTs, uint16_t width) { fAvRTPool.resize(numRTs); @@ -1527,7 +1826,7 @@ bool pl3DPipeline::IFillAvRTPool(uint16_t numRTs, uint16_t width) } -template +template void pl3DPipeline::IReleaseAvRTPool() { for (plClothingOutfit* outfit : fClothingOutfits) @@ -1543,14 +1842,14 @@ void pl3DPipeline::IReleaseAvRTPool() } -template +template plRenderTarget *pl3DPipeline::IGetNextAvRT() { return fAvRTPool[fAvNextFreeRT++]; } -template +template void pl3DPipeline::IFreeAvRT(plRenderTarget* tex) { auto iter = std::find(fAvRTPool.begin(), fAvRTPool.end(), tex); @@ -1563,7 +1862,79 @@ void pl3DPipeline::IFreeAvRT(plRenderTarget* tex) } -template +template +plLayerInterface* pl3DPipeline::IPushOverBaseLayer(plLayerInterface* li) +{ + if (!li) + return nullptr; + + fOverLayerStack.push_back(li); + + if (!fOverBaseLayer) + return fOverBaseLayer = li; + + fForceMatHandle = true; + fOverBaseLayer = fOverBaseLayer->Attach(li); + fOverBaseLayer->Eval(fTime, fFrame, 0); + return fOverBaseLayer; +} + + +template +plLayerInterface* pl3DPipeline::IPopOverBaseLayer(plLayerInterface* li) +{ + if (!li) + return nullptr; + + fForceMatHandle = true; + + plLayerInterface* pop = fOverLayerStack.back(); + fOverLayerStack.pop_back(); + fOverBaseLayer = fOverBaseLayer->Detach(pop); + + return pop; +} + + +template +plLayerInterface* pl3DPipeline::IPushOverAllLayer(plLayerInterface* li) +{ + if (!li) + return nullptr; + + fOverLayerStack.push_back(li); + + if (!fOverAllLayer) { + fOverAllLayer = li; + fOverAllLayer->Eval(fTime, fFrame, 0); + return fOverAllLayer; + } + + fForceMatHandle = true; + fOverAllLayer = fOverAllLayer->Attach(li); + fOverAllLayer->Eval(fTime, fFrame, 0); + + return fOverAllLayer; +} + + +template +plLayerInterface* pl3DPipeline::IPopOverAllLayer(plLayerInterface* li) +{ + if (!li) + return nullptr; + + fForceMatHandle = true; + + plLayerInterface* pop = fOverLayerStack.back(); + fOverLayerStack.pop_back(); + fOverAllLayer = fOverAllLayer->Detach(pop); + + return pop; +} + + +template void pl3DPipeline::ICheckLighting(plDrawableSpans* drawable, std::vector& visList, plVisMgr* visMgr) { if (fView.fRenderState & kRenderNoLights) @@ -1794,7 +2165,7 @@ void pl3DPipeline::ICheckLighting(plDrawableSpans* drawable, std::ve } -template +template hsMatrix44 pl3DPipeline::IGetCameraToNDC() { hsMatrix44 cam2ndc = GetViewTransform().GetCameraToNDC(); @@ -1832,7 +2203,7 @@ hsMatrix44 pl3DPipeline::IGetCameraToNDC() } -template +template void pl3DPipeline::ISetLocalToWorld(const hsMatrix44& l2w, const hsMatrix44& w2l) { fView.SetLocalToWorld(l2w); @@ -1848,7 +2219,7 @@ void pl3DPipeline::ISetLocalToWorld(const hsMatrix44& l2w, const hsM -template +template void pl3DPipeline::ITransformsToDevice() { if (fView.fXformResetFlags & fView.kResetCamera) @@ -1862,14 +2233,14 @@ void pl3DPipeline::ITransformsToDevice() } -template +template void pl3DPipeline::IProjectionMatrixToDevice() { fDevice.SetProjectionMatrix(IGetCameraToNDC()); fView.fXformResetFlags &= ~fView.kResetProjection; } -template +template void pl3DPipeline::IWorldToCameraToDevice() { fDevice.SetWorldToCameraMatrix(fView.GetWorldToCamera()); @@ -1878,11 +2249,143 @@ void pl3DPipeline::IWorldToCameraToDevice() fFrame++; } -template +template void pl3DPipeline::ILocalToWorldToDevice() { fDevice.SetLocalToWorldMatrix(fView.GetLocalToWorld()); fView.fXformResetFlags &= ~fView.kResetL2W; } +struct plSortFace +{ + uint16_t fIdx[3]; + float fDist; +}; + +struct plCompSortFace +{ + bool operator()( const plSortFace& lhs, const plSortFace& rhs) const + { + return lhs.fDist > rhs.fDist; + } +}; + +template +bool pl3DPipeline::IAvatarSort(plDrawableSpans* d, const std::vector& visList) +{ + plProfile_BeginTiming(AvatarSort); + for (int16_t visIdx : visList) + { + hsAssert(d->GetSpan(visIdx)->fTypeMask & plSpan::kIcicleSpan, "Unknown type for sorting faces"); + + plIcicle* span = (plIcicle*)d->GetSpan(visIdx); + + if (span->fProps & plSpan::kPartialSort) { + hsAssert(d->GetBufferGroup(span->fGroupIdx)->AreIdxVolatile(), "Badly setup buffer group - set PartialSort too late?"); + + const hsPoint3 viewPos = GetViewPositionWorld(); + + plGBufferGroup* group = d->GetBufferGroup(span->fGroupIdx); + + typename DeviceType::VertexBufferRef* vRef = static_cast(group->GetVertexBufferRef(span->fVBufferIdx)); + + const uint8_t* vdata = vRef->fData; + const uint32_t stride = vRef->fVertexSize; + + const int numTris = span->fILength/3; + + static std::vector sortScratch; + sortScratch.resize(numTris); + + plProfile_IncCount(AvatarFaces, numTris); + + // Have three very similar sorts here, differing only on where the "position" of + // each triangle is defined, either as the center of the triangle, the nearest + // point on the triangle, or the farthest point on the triangle. + // Having tried all three on the avatar (the only thing this sort is used on), + // the best results surprisingly came from using the center of the triangle. + uint16_t* indices = group->GetIndexBufferData(span->fIBufferIdx) + span->fIStartIdx; + int j; + for( j = 0; j < numTris; j++ ) + { +#if 1 // TRICENTER + uint16_t idx = *indices++; + sortScratch[j].fIdx[0] = idx; + hsPoint3 pos = *(hsPoint3*)(vdata + idx * stride); + + idx = *indices++; + sortScratch[j].fIdx[1] = idx; + pos += *(hsPoint3*)(vdata + idx * stride); + + idx = *indices++; + sortScratch[j].fIdx[2] = idx; + pos += *(hsPoint3*)(vdata + idx * stride); + + pos *= 0.3333f; + + sortScratch[j].fDist = hsVector3(&pos, &viewPos).MagnitudeSquared(); +#elif 0 // NEAREST + uint16_t idx = *indices++; + sortScratch[j].fIdx[0] = idx; + hsPoint3 pos = *(hsPoint3*)(vdata + idx * stride); + float dist = hsVector3(&pos, &viewPos).MagnitudeSquared(); + float minDist = dist; + + idx = *indices++; + sortScratch[j].fIdx[1] = idx; + pos = *(hsPoint3*)(vdata + idx * stride); + dist = hsVector3(&pos, &viewPos).MagnitudeSquared(); + if( dist < minDist ) + minDist = dist; + + idx = *indices++; + sortScratch[j].fIdx[2] = idx; + pos = *(hsPoint3*)(vdata + idx * stride); + dist = hsVector3(&pos, &viewPos).MagnitudeSquared(); + if( dist < minDist ) + minDist = dist; + + sortScratch[j].fDist = minDist; +#elif 1 // FURTHEST + uint16_t idx = *indices++; + sortScratch[j].fIdx[0] = idx; + hsPoint3 pos = *(hsPoint3*)(vdata + idx * stride); + float dist = hsVector3(&pos, &viewPos).MagnitudeSquared(); + float maxDist = dist; + + idx = *indices++; + sortScratch[j].fIdx[1] = idx; + pos = *(hsPoint3*)(vdata + idx * stride); + dist = hsVector3(&pos, &viewPos).MagnitudeSquared(); + if( dist > maxDist ) + maxDist = dist; + + idx = *indices++; + sortScratch[j].fIdx[2] = idx; + pos = *(hsPoint3*)(vdata + idx * stride); + dist = hsVector3(&pos, &viewPos).MagnitudeSquared(); + if( dist > maxDist ) + maxDist = dist; + + sortScratch[j].fDist = maxDist; +#endif // SORTTYPES + } + + std::sort(sortScratch.begin(), sortScratch.end(), plCompSortFace()); + + indices = group->GetIndexBufferData(span->fIBufferIdx) + span->fIStartIdx; + for (const plSortFace& iter : sortScratch) + { + *indices++ = iter.fIdx[0]; + *indices++ = iter.fIdx[1]; + *indices++ = iter.fIdx[2]; + } + + group->DirtyIndexBuffer(span->fIBufferIdx); + } + } + plProfile_EndTiming(AvatarSort); + return true; +} + #endif //_pl3DPipeline_inc_ diff --git a/Sources/Plasma/PubUtilLib/plPipeline/plNullPipeline.h b/Sources/Plasma/PubUtilLib/plPipeline/plNullPipeline.h index 72c29391c0..4ad36caf02 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/plNullPipeline.h +++ b/Sources/Plasma/PubUtilLib/plPipeline/plNullPipeline.h @@ -48,14 +48,40 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com class plNullPipelineDevice { + class NullDeviceRef : public hsGDeviceRef + { + public: + void* fData; + uint32_t fCount; + uint32_t fVertexSize; + uint32_t fFormat; + + void Release() { } + void Link(NullDeviceRef** back) { } + void Unlink() { } + bool IsLinked() { return true; } + bool Volatile() const { return false; } + }; + public: - typedef void VertexBufferRef; - typedef void IndexBufferRef; - typedef void TextureRef; + typedef NullDeviceRef VertexBufferRef; + typedef NullDeviceRef IndexBufferRef; + typedef NullDeviceRef TextureRef; bool InitDevice() { return true; } void SetRenderTarget(plRenderTarget* target) { } void SetViewport() { } + void SetupVertexBufferRef(plGBufferGroup* owner, uint32_t idx, VertexBufferRef* vRef) { } + void CheckStaticVertexBuffer(VertexBufferRef* vRef, plGBufferGroup* owner, uint32_t idx) { } + void FillStaticVertexBufferRef(VertexBufferRef* ref, plGBufferGroup* group, uint32_t idx) { } + void FillVolatileVertexBufferRef(VertexBufferRef* ref, plGBufferGroup* group, uint32_t idx) { } + void SetupIndexBufferRef(plGBufferGroup* owner, uint32_t idx, IndexBufferRef* iRef) { } + void CheckIndexBuffer(IndexBufferRef* iRef) { } + void FillIndexBufferRef(IndexBufferRef* iRef, plGBufferGroup* owner, uint32_t idx) { } + void SetupTextureRef(plLayerInterface* layer, plBitmap* img, TextureRef* tRef) {} + void CheckTexture(TextureRef* tRef) {} + void MakeTextureRef(TextureRef* tRef, plLayerInterface* layer, plMipmap* img) {} + void MakeCubicTextureRef(TextureRef* tRef, plLayerInterface* layer, plCubicEnvironmap* img) {} void SetProjectionMatrix(const hsMatrix44& src) { } void SetWorldToCameraMatrix(const hsMatrix44& src) { } void SetLocalToWorldMatrix(const hsMatrix44& src) { } diff --git a/Sources/Plasma/PubUtilLib/plPipeline/plPipelineViewSettings.h b/Sources/Plasma/PubUtilLib/plPipeline/plPipelineViewSettings.h index 57df71ecf8..b8ec99bb1f 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/plPipelineViewSettings.h +++ b/Sources/Plasma/PubUtilLib/plPipeline/plPipelineViewSettings.h @@ -151,6 +151,7 @@ class plPipelineViewSettings uint32_t GetSubDrawableTypeMask() const { return fSubDrawableTypeMask; } void SetSubDrawableTypeMask(uint32_t mask) { fSubDrawableTypeMask = mask; } + bool IsViewLeftHanded() const { return (GetConstViewTransform().GetOrthogonal() ^ (fLocalToWorldLeftHanded ^ fWorldToCamLeftHanded)) != 0; } /** Initialize the ViewSettings to default (normal/neutral) values. */ void Reset(plPipeline* pipeline); diff --git a/Sources/Plasma/PubUtilLib/plSurface/hsGMaterial.cpp b/Sources/Plasma/PubUtilLib/plSurface/hsGMaterial.cpp index e344d3e19d..e61d54f08c 100644 --- a/Sources/Plasma/PubUtilLib/plSurface/hsGMaterial.cpp +++ b/Sources/Plasma/PubUtilLib/plSurface/hsGMaterial.cpp @@ -47,6 +47,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "HeadSpin.h" #include "plProfile.h" #include "hsResMgr.h" +#include "hsGDeviceRef.h" #include "plLayer.h" #include "plLayerInterface.h" @@ -61,18 +62,17 @@ plLayer defaultLayer; ////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////// -hsGMaterial::hsGMaterial() : -fLOD(0), -fCompFlags(0), -fLoadFlags(0), -fLastUpdateTime(0), -fDeviceRef() +hsGMaterial::hsGMaterial() + : fLOD(), fCompFlags(), fLoadFlags(), fLastUpdateTime(), fDeviceRef() { } hsGMaterial::~hsGMaterial() { IClearLayers(); + + if (fDeviceRef != nullptr) + hsRefCnt_SafeUnRef(fDeviceRef); } plLayerInterface* hsGMaterial::GetPiggyBack(size_t which) diff --git a/Sources/Plasma/PubUtilLib/plSurface/hsGMaterial.h b/Sources/Plasma/PubUtilLib/plSurface/hsGMaterial.h index 2f411d1b4a..e17353f9fc 100644 --- a/Sources/Plasma/PubUtilLib/plSurface/hsGMaterial.h +++ b/Sources/Plasma/PubUtilLib/plSurface/hsGMaterial.h @@ -52,7 +52,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com class hsScene; class hsResMgr; -class hsG3DDevice; +class hsGDeviceRef; class plLayerInterface; class plLayer; @@ -84,15 +84,14 @@ class hsGMaterial : public plSynchedObject }; protected: - uint32_t fLOD; - std::vector fLayers; - std::vector fPiggyBacks; + uint32_t fLOD; + std::vector fLayers; + std::vector fPiggyBacks; - uint32_t fCompFlags; - uint32_t fLoadFlags; + uint32_t fCompFlags; + uint32_t fLoadFlags; - float fLastUpdateTime; - + float fLastUpdateTime; hsGDeviceRef* fDeviceRef; void IClearLayers(); @@ -131,8 +130,8 @@ class hsGMaterial : public plSynchedObject bool IsDynamic() const { return (fCompFlags & kCompDynamic); } bool IsDecal() const { return (fCompFlags & kCompDecal); } bool NeedsBlendChannel() { return (fCompFlags & kCompNeedsBlendChannel); } - - + + void SetDeviceRef(hsGDeviceRef* ref) { hsRefCnt_SafeAssign(fDeviceRef, ref); } hsGDeviceRef* GetDeviceRef() const { return fDeviceRef; }