summaryrefslogtreecommitdiff
path: root/Runtime/GfxDevice/GfxDevice.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/GfxDevice/GfxDevice.cpp')
-rw-r--r--Runtime/GfxDevice/GfxDevice.cpp926
1 files changed, 926 insertions, 0 deletions
diff --git a/Runtime/GfxDevice/GfxDevice.cpp b/Runtime/GfxDevice/GfxDevice.cpp
new file mode 100644
index 0000000..a1837df
--- /dev/null
+++ b/Runtime/GfxDevice/GfxDevice.cpp
@@ -0,0 +1,926 @@
+#include "UnityPrefix.h"
+#include "GfxDevice.h"
+#include "Runtime/Utilities/HashFunctions.h"
+#include "Runtime/Filters/Mesh/MeshSkinning.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Dynamics/PhysicsManager.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Graphics/RenderSurface.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/Threads/AtomicOps.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Misc/Plugins.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "ChannelAssigns.h"
+#include "BatchRendering.h"
+#include "GpuProgram.h"
+#include "Runtime/Geometry/BoundingUtils.h"
+
+#if ENABLE_TEXTUREID_MAP
+ #include "TextureIdMap.h"
+#endif
+
+#if ENABLE_SPRITES
+ #include "Runtime/Graphics/SpriteFrame.h"
+#endif
+
+class VBOList
+{
+public:
+ List<VBO> m_List;
+};
+
+static GfxDevice* gfxDevice = NULL;
+
+#if ENABLE_MULTITHREADED_CODE
+static GfxDevice* realGfxDevice = NULL;
+static Thread::ThreadID realGfxDeviceThreadId;
+static GfxThreadingMode gfxThreadingMode = kGfxThreadingModeDirect;
+#endif
+
+void ApplyTexEnvData (unsigned int texUnit, unsigned int samplerUnit, const TexEnvData& data)
+{
+ GfxDevice& device = GetRealGfxDevice();
+
+ device.SetTexture (kShaderFragment, texUnit, samplerUnit, data.textureID, static_cast<TextureDimension>(data.texDim), data.mipBias);
+ // Only setup texture matrix & transform for texture units that fit into supported
+ // coordinate count. Shaders can use more textures,
+ // but then they can't have T&L matrices nor fixed function texgen.
+ if (texUnit < kMaxSupportedTextureCoords)
+ {
+ device.SetTextureTransform (texUnit, static_cast<TextureDimension>(data.texDim), static_cast<TexGenMode>(data.texGen), data.identityMatrix, data.matrix.GetPtr());
+ }
+}
+
+void ClearStaticBatchIndices();
+
+bool IsGfxDevice()
+{
+ return gfxDevice != NULL;
+}
+
+GfxDevice& GetGfxDevice()
+{
+ Assert( gfxDevice );
+#if ENABLE_MULTITHREADED_CODE
+ DebugAssert(realGfxDevice == NULL || Thread::CurrentThreadIsMainThread());
+#endif
+ return *gfxDevice;
+}
+
+GfxDevice& GetUncheckedGfxDevice()
+{
+ return *gfxDevice;
+}
+
+void SetGfxDevice(GfxDevice* device)
+{
+ gfxDevice = device;
+}
+
+void DestroyGfxDevice()
+{
+ if (gfxDevice)
+ {
+ UNITY_DELETE(gfxDevice, kMemGfxDevice);
+ gfxDevice = NULL;
+ }
+}
+
+GfxDevice& GetRealGfxDevice()
+{
+#if ENABLE_MULTITHREADED_CODE
+ if (realGfxDevice)
+ {
+ DebugAssert(Thread::EqualsCurrentThreadIDForAssert(realGfxDeviceThreadId));
+ return *realGfxDevice;
+ }
+#endif
+ return *gfxDevice;
+}
+
+bool IsRealGfxDeviceThreadOwner()
+{
+#if ENABLE_MULTITHREADED_CODE
+ if (realGfxDevice)
+ return Thread::EqualsCurrentThreadIDForAssert(realGfxDeviceThreadId);
+#endif
+ return true;
+}
+
+#if ENABLE_MULTITHREADED_CODE
+void SetRealGfxDevice(GfxDevice* device)
+{
+ Assert( !realGfxDevice );
+ realGfxDevice = device;
+ SetRealGfxDeviceThreadOwnership();
+}
+
+void SetRealGfxDeviceThreadOwnership()
+{
+ realGfxDeviceThreadId = Thread::GetCurrentThreadID();
+}
+
+void DestroyRealGfxDevice()
+{
+ if (realGfxDevice)
+ {
+ UNITY_DELETE(realGfxDevice, kMemGfxThread);
+ realGfxDevice = NULL;
+ }
+}
+
+void SetGfxThreadingMode(GfxThreadingMode mode)
+{
+ gfxThreadingMode = mode;
+}
+
+GfxThreadingMode GetGfxThreadingMode()
+{
+ return gfxThreadingMode;
+}
+#endif
+
+#if GFX_DEVICE_VIRTUAL
+GfxDevice::GfxDevice()
+{
+ OnCreate();
+}
+
+GfxDevice::~GfxDevice()
+{
+ OnDelete();
+}
+#endif
+
+void GfxDevice::OnCreate()
+{
+ m_Stats.ResetFrame();
+ m_SavedStats.ResetFrame();
+ m_ActiveRenderTexture = NULL;
+ m_InsideFrame = false;
+ m_IsRecording = false;
+ m_IsThreadable = false;
+ m_FramebufferDepthFormat = kDepthFormatNone;
+ for (int i = 0; i < kShaderTypeCount; ++i)
+ m_BuiltinParamIndices[i] = &m_NullParamIndices;
+ m_VBOList = new VBOList;
+
+#if ENABLE_TEXTUREID_MAP
+ TextureIdMap::Initialize();
+#endif
+}
+
+void GfxDevice::OnDelete()
+{
+ delete m_VBOList;
+
+#if ENABLE_TEXTUREID_MAP
+ TextureIdMap::Uninitialize();
+#endif
+
+ ClearStaticBatchIndices();
+}
+
+void GfxDevice::OnCreateVBO(VBO* vbo)
+{
+ SET_ALLOC_OWNER(this);
+ m_VBOList->m_List.push_back(*vbo);
+}
+
+void GfxDevice::OnDeleteVBO(VBO* vbo)
+{
+ m_VBOList->m_List.erase(vbo);
+}
+
+int GfxDevice::GetTotalVBOCount() const
+{
+ int count = 0;
+ List<VBO>::iterator itr, end = m_VBOList->m_List.end();
+ for (itr = m_VBOList->m_List.begin(); itr != end; ++itr)
+ {
+ if (!itr->GetHideFromRuntimeStats())
+ ++count;
+ }
+ return count;
+}
+
+int GfxDevice::GetTotalVBOBytes() const
+{
+ int size = 0;
+ List<VBO>::iterator itr, end = m_VBOList->m_List.end();
+ for (itr = m_VBOList->m_List.begin(); itr != end; ++itr)
+ {
+ if (!itr->GetHideFromRuntimeStats())
+ size += itr->GetRuntimeMemorySize();
+ }
+ return size;
+}
+
+void GfxDevice::RecreateAllVBOs()
+{
+ List<VBO>::iterator itr, end = m_VBOList->m_List.end();
+ for (itr = m_VBOList->m_List.begin() ; itr != end; ++itr)
+ {
+ itr->Recreate();
+ }
+ GetDynamicVBO().Recreate();
+}
+
+#if GFX_SUPPORTS_D3D9
+void GfxDevice::ResetDynamicVBs()
+{
+ List<VBO>::iterator itr, end = m_VBOList->m_List.end();
+ for (itr = m_VBOList->m_List.begin() ; itr != end; ++itr)
+ {
+ itr->ResetDynamicVB();
+ }
+}
+#endif
+
+#if GFX_SUPPORTS_OPENGLES20
+void GfxDevice::MarkAllVBOsLost()
+{
+ for(List<VBO>::iterator itr = m_VBOList->m_List.begin(), end = m_VBOList->m_List.end() ; itr != end; ++itr)
+ itr->MarkBuffersLost();
+}
+#endif
+
+void GfxDevice::SetWorldMatrixAndType( const float matrix[16], TransformType type )
+{
+ SetWorldMatrix(matrix);
+ bool backface = (type & kOddNegativeScaleTransform) != 0;
+ int normalization = (type & kUniformScaleTransform) ? kNormalizationScale : 0;
+ normalization |= (type & kNonUniformScaleTransform) ? kNormalizationFull : 0;
+ DebugAssert(normalization != (kNormalizationScale|kNormalizationFull));
+ SetNormalizationBackface(NormalizationMode(normalization), backface);
+}
+
+void GfxDevice::SetInverseScale (float invScale)
+{
+ m_BuiltinParamValues.SetInstanceVectorParam(kShaderInstanceVecScale, Vector4f(0,0,0, invScale));
+}
+
+GpuProgram* GfxDevice::CreateGpuProgram( const std::string& source, CreateGpuProgramOutput& output )
+{
+ return ::CreateGpuProgram( source, output );
+}
+
+void GfxDevice::RecordSetBlendState(const DeviceBlendState* state, const ShaderLab::FloatVal& alphaRef, const ShaderLab::PropertySheet* props )
+{
+ ErrorString("GfxDevice does not support recording");
+}
+
+void GfxDevice::RecordSetMaterial( const ShaderLab::VectorVal& ambient, const ShaderLab::VectorVal& diffuse, const ShaderLab::VectorVal& specular, const ShaderLab::VectorVal& emissive, const ShaderLab::FloatVal& shininess, const ShaderLab::PropertySheet* props )
+{
+ ErrorString("GfxDevice does not support recording");
+}
+
+void GfxDevice::RecordSetColor( const ShaderLab::VectorVal& color, const ShaderLab::PropertySheet* props )
+{
+ ErrorString("GfxDevice does not support recording");
+}
+
+void GfxDevice::RecordEnableFog( FogMode fogMode, const ShaderLab::FloatVal& fogStart, const ShaderLab::FloatVal& fogEnd, const ShaderLab::FloatVal& fogDensity, const ShaderLab::VectorVal& fogColor, const ShaderLab::PropertySheet* props )
+{
+ ErrorString("GfxDevice does not support recording");
+}
+
+void GfxDevice::SetMaterialProperties(const MaterialPropertyBlock& block)
+{
+ m_MaterialProperties = block;
+}
+
+
+
+struct SkinMeshTask
+{
+ SkinMeshInfo info;
+ VBO* vbo;
+ void* vboMemory;
+};
+
+#if ENABLE_MULTITHREADED_SKINNING
+static JobScheduler::JobGroupID s_SkinJobGroup;
+#endif
+static dynamic_array<SkinMeshTask> s_ActiveSkins;
+static bool s_InsideSkinning = false;
+
+void EndSkinTask(SkinMeshTask& task)
+{
+ if (task.vbo)
+ task.vbo->UnmapVertexStream(0);
+ task.info.Release();
+}
+
+void GfxDevice::BeginSkinning( int maxSkinCount )
+{
+ Assert(!s_InsideSkinning);
+#if ENABLE_MULTITHREADED_SKINNING
+ s_SkinJobGroup = GetJobScheduler().BeginGroup(maxSkinCount);
+#endif
+ s_ActiveSkins.reserve(maxSkinCount);
+ s_InsideSkinning = true;
+}
+
+bool GfxDevice::SkinMesh( const SkinMeshInfo& skin, VBO* vbo )
+{
+ Assert(s_InsideSkinning);
+ Assert((vbo == NULL) != (skin.outVertices == NULL));
+ VertexStreamData mappedVSD;
+ if (vbo && !vbo->MapVertexStream(mappedVSD, 0))
+ {
+ // Bail out before we push to active skins
+ skin.Release();
+ return false;
+ }
+
+ // Array must be preallocated to at least the right size
+ Assert(s_ActiveSkins.size() < s_ActiveSkins.capacity());
+ int skinIndex = s_ActiveSkins.size();
+ s_ActiveSkins.resize_uninitialized(skinIndex + 1);
+ SkinMeshTask& task = s_ActiveSkins[skinIndex];
+ task.info = skin;
+ task.vbo = vbo;
+
+ // Caller passes in a buffer if it wants to read it back
+ // Otherwise skin directly to VBO memory
+ if (vbo)
+ task.info.outVertices = mappedVSD.buffer;
+
+#if ENABLE_MULTITHREADED_SKINNING
+ GetJobScheduler().SubmitJob(s_SkinJobGroup, DeformSkinnedMeshJob, &task.info, NULL);
+#else
+ DeformSkinnedMesh(task.info);
+ EndSkinTask(task);
+#endif
+ return true;
+}
+
+void GfxDevice::EndSkinning()
+{
+ Assert(s_InsideSkinning);
+#if ENABLE_MULTITHREADED_SKINNING
+ GetJobScheduler().WaitForGroup(s_SkinJobGroup);
+ for (int i = 0; i < s_ActiveSkins.size(); i++)
+ EndSkinTask(s_ActiveSkins[i]);
+#endif
+ s_ActiveSkins.resize_uninitialized(0);
+ s_InsideSkinning = false;
+}
+
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+struct StaticBatch
+{
+#if GFX_SUPPORTS_OPENGLES20 && GFX_OPENGLESxx_ONLY
+ enum { kMaxIndexCount = 16384 * 3 };
+#else // everything else
+ // Anything over 32k causes a slowdown on MBPs with AMD cards (Case 394520)
+ enum { kMaxIndexCount = 32000 };
+#endif
+ ~StaticBatch() { UNITY_FREE( kMemBatchedGeometry, indices); }
+ bool isActive;
+ ABSOLUTE_TIME startTime;
+ ChannelAssigns channels;
+ size_t indexCount;
+ size_t vertexCount;
+ size_t meshCount;
+ GfxPrimitiveType topology;
+ size_t vertexRangeBegin;
+ size_t vertexRangeEnd;
+ UInt16* indices;
+} s_StaticBatch;
+
+void ClearStaticBatchIndices()
+{
+ UNITY_FREE(kMemBatchedGeometry,s_StaticBatch.indices);
+ s_StaticBatch.indices = NULL;
+}
+
+int GfxDevice::GetMaxStaticBatchIndices()
+{
+ return StaticBatch::kMaxIndexCount;
+}
+
+void GfxDevice::BeginStaticBatching (const ChannelAssigns& channels, GfxPrimitiveType topology)
+{
+ Assert(!s_StaticBatch.isActive);
+ Assert(topology==kPrimitiveTriangles || topology==kPrimitiveTriangleStripDeprecated);
+
+ StaticBatch& batch = s_StaticBatch;
+ if (!batch.indices)
+ {
+ const size_t ibSize = StaticBatch::kMaxIndexCount * kVBOIndexSize;
+ batch.indices = reinterpret_cast<UInt16*>(UNITY_MALLOC_ALIGNED( kMemBatchedGeometry, ibSize, 32));
+ }
+ batch.startTime = START_TIME;
+ batch.channels = channels;
+ batch.indexCount = 0;
+ batch.vertexCount = 0;
+ batch.meshCount = 0;
+ batch.topology = topology;
+ batch.vertexRangeBegin = std::numeric_limits<size_t>::max();
+ batch.vertexRangeEnd = 0;
+ batch.isActive = true;
+}
+
+void GfxDevice::StaticBatchMesh( UInt32 firstVertex, UInt32 vertexCount, const IndexBufferData& indices, UInt32 firstIndexByte, UInt32 indexCount )
+{
+ Assert(s_StaticBatch.isActive);
+
+ StaticBatch& batch = s_StaticBatch;
+ batch.vertexCount += vertexCount;
+ batch.vertexRangeBegin = std::min<size_t>(batch.vertexRangeBegin, firstVertex);
+ batch.vertexRangeEnd = std::max<size_t>(batch.vertexRangeEnd, firstVertex + vertexCount);
+ const UInt16* srcIndices = reinterpret_cast<const UInt16*>(static_cast<const UInt8*>(indices.indices) + firstIndexByte);
+ AppendMeshIndices(batch.indices, batch.indexCount, srcIndices, indexCount, batch.topology==kPrimitiveTriangleStripDeprecated);
+ batch.meshCount++;
+}
+
+void GfxDevice::EndStaticBatching( VBO& vbo, const Matrix4x4f& matrix, TransformType transformType, int sourceChannels )
+{
+ Assert(s_StaticBatch.isActive);
+
+ SetWorldMatrixAndType(matrix.GetPtr(), transformType);
+ const StaticBatch& batch = s_StaticBatch;
+ vbo.DrawCustomIndexed(batch.channels, batch.indices, batch.indexCount, batch.topology,
+ batch.vertexRangeBegin, batch.vertexRangeEnd, batch.vertexCount);
+
+ ABSOLUTE_TIME elapsedTime = ELAPSED_TIME(batch.startTime);
+ int primCount = GetPrimitiveCount(batch.indexCount, batch.topology, false);
+ GetFrameStats().AddBatch(primCount, batch.vertexCount, batch.meshCount, elapsedTime);
+ s_StaticBatch.isActive = false;
+}
+
+struct DynamicBatch
+{
+ bool isActive;
+ ABSOLUTE_TIME startTime;
+ ChannelAssigns shaderChannels;
+ UInt32 availableChannels;
+ size_t maxVertices;
+ size_t maxIndices;
+ size_t vertexCount;
+ size_t indexCount;
+ size_t meshCount;
+ GfxPrimitiveType topology;
+ size_t destStride;
+ UInt8* outVertices;
+ UInt16* outIndices;
+} s_DynamicBatch;
+
+void GfxDevice::BeginDynamicBatching( const ChannelAssigns& shaderChannels, UInt32 availableChannels, size_t maxVertices, size_t maxIndices, GfxPrimitiveType topology)
+{
+ Assert(!s_DynamicBatch.isActive);
+ Assert(topology != kPrimitiveLineStrip);
+
+ DynamicBatch& batch = s_DynamicBatch;
+ batch.startTime = START_TIME;
+ batch.shaderChannels = shaderChannels;
+ batch.availableChannels = availableChannels;
+ batch.maxVertices = maxVertices;
+ batch.maxIndices = (topology == kPrimitiveQuads) ? maxIndices/4*6 : maxIndices;
+ batch.vertexCount = 0;
+ batch.indexCount = 0;
+ batch.meshCount = 0;
+ batch.topology = topology;
+
+ batch.destStride = 0;
+ for( int i = 0; i < kShaderChannelCount; ++i )
+ if( availableChannels & (1<<i) )
+ batch.destStride += VBO::GetDefaultChannelByteSize(i);
+
+ DynamicVBO::RenderMode renderMode;
+ switch (topology)
+ {
+ case kPrimitiveTriangleStripDeprecated:
+ renderMode = DynamicVBO::kDrawIndexedTriangleStrip;
+ break;
+ case kPrimitiveLines:
+ renderMode = DynamicVBO::kDrawIndexedLines;
+ break;
+ case kPrimitiveQuads:
+ renderMode = DynamicVBO::kDrawIndexedQuads;
+ break;
+ case kPrimitivePoints:
+ renderMode = DynamicVBO::kDrawIndexedPoints;
+ break;
+ default:
+ renderMode = DynamicVBO::kDrawIndexedTriangles;
+ break;
+ }
+
+ // Get VBO chunk
+ batch.isActive = GetDynamicVBO().GetChunk(
+ availableChannels, maxVertices, batch.maxIndices, renderMode,
+ (void**)&batch.outVertices, (void**)&batch.outIndices);
+}
+
+void GfxDevice::DynamicBatchMesh( const Matrix4x4f& matrix, const VertexBufferData& vertices, UInt32 firstVertex, UInt32 vertexCount, const IndexBufferData& indices, UInt32 firstIndexByte, UInt32 indexCount )
+{
+ Assert(s_DynamicBatch.isActive);
+ DynamicBatch& batch = s_DynamicBatch;
+ size_t outIndexCount;
+
+ // convert quad indices to triangle indices
+ if (batch.topology == kPrimitiveQuads)
+ {
+ int quadIndexCount = indexCount/4*6;
+ UInt16* quadIB = ALLOC_TEMP_MANUAL (UInt16, quadIndexCount);
+ UInt16* src = (UInt16*)((UInt8*)indices.indices + firstIndexByte);
+ Prefetch(src, indexCount * kVBOIndexSize);
+ for (int i = 0; i < indexCount/4; ++i)
+ {
+ quadIB[6*i+0] = src[0];
+ quadIB[6*i+1] = src[1];
+ quadIB[6*i+2] = src[2];
+ quadIB[6*i+3] = src[0];
+ quadIB[6*i+4] = src[2];
+ quadIB[6*i+5] = src[3];
+ src += 4;
+ }
+ outIndexCount = TransformIndices(batch.outIndices, quadIB, 0, quadIndexCount, firstVertex, batch.vertexCount, false);
+ FREE_TEMP_MANUAL(quadIB);
+ }
+ else
+ outIndexCount = TransformIndices(batch.outIndices, indices.indices, firstIndexByte, indexCount, firstVertex, batch.vertexCount, batch.topology==kPrimitiveTriangleStripDeprecated);
+
+ size_t outVertexCount = TransformVertices(batch.outVertices, matrix, vertices, firstVertex, vertexCount, batch.availableChannels);
+
+ batch.outIndices += outIndexCount;
+ batch.outVertices += outVertexCount * batch.destStride;
+ batch.indexCount += outIndexCount;
+ batch.vertexCount += outVertexCount;
+ batch.meshCount++;
+}
+
+void GfxDevice::EndDynamicBatching( TransformType transformType )
+{
+ Assert(s_DynamicBatch.isActive);
+
+ const DynamicBatch& batch = s_DynamicBatch;
+ Assert(batch.vertexCount <= batch.maxVertices);
+ Assert(batch.indexCount <= batch.maxIndices);
+
+ // Release VBO chunk
+ GetDynamicVBO().ReleaseChunk(batch.vertexCount, batch.indexCount);
+
+ SetWorldMatrixAndType(Matrix4x4f::identity.GetPtr(), transformType);
+
+ GetDynamicVBO().DrawChunk(batch.shaderChannels);
+ ABSOLUTE_TIME elapsedTime = ELAPSED_TIME(batch.startTime);
+ int primCount = GetPrimitiveCount(batch.indexCount, batch.topology, false);
+ GetFrameStats().AddBatch(primCount, batch.vertexCount, batch.meshCount, elapsedTime);
+ s_DynamicBatch.isActive = false;
+}
+#if ENABLE_SPRITES
+void GfxDevice::DynamicBatchSprite(const Matrix4x4f* matrix, const SpriteRenderData* rd, ColorRGBA32 color)
+{
+ Assert(s_DynamicBatch.isActive);
+ DynamicBatch& batch = s_DynamicBatch;
+
+ TransformSprite (batch.outVertices, batch.outIndices, matrix, rd, color, batch.vertexCount);
+ int outIndexCount = (int)rd->indices.size();
+ int outVertexCount = (int)rd->vertices.size();
+ batch.outIndices += outIndexCount;
+ batch.outVertices += outVertexCount * batch.destStride;
+ batch.indexCount += outIndexCount;
+ batch.vertexCount += outVertexCount;
+ batch.meshCount++;
+}
+#endif
+#else
+void ClearStaticBatchIndices(){}
+#endif //GFX_ENABLE_DRAW_CALL_BATCHING
+
+void GfxDevice::AddBatchingStats( int batchedTris, int batchedVerts, int batchedCalls )
+{
+ ABSOLUTE_TIME unusedTime;
+ ABSOLUTE_TIME_INIT(unusedTime);
+ GetFrameStats().AddBatch(batchedTris, batchedVerts, batchedCalls, unusedTime);
+}
+
+// on gl/gles we create textures at the very beginning with glGenTextures
+// so start generate ids from smth not 0
+volatile int GfxDevice::ms_TextureIDGenerator = 10;
+volatile int GfxDevice::ms_ComputeBufferIDGenerator = 0;
+
+#if !UNITY_WII // Wii also needs to register the ID, so separate impl
+TextureID GfxDevice::CreateTextureID ()
+{
+ return TextureID(AtomicIncrement(&ms_TextureIDGenerator));
+}
+#endif
+
+void GfxDevice::FreeTextureID( TextureID texture )
+{
+}
+
+ComputeBufferID GfxDevice::CreateComputeBufferID()
+{
+ return ComputeBufferID(AtomicIncrement (&ms_ComputeBufferIDGenerator));
+}
+
+void GfxDevice::FreeComputeBufferID(ComputeBufferID id)
+{
+ // Do nothing yet
+}
+
+
+void GfxDevice::ResetFrameStats()
+{
+ m_Stats.ResetFrame();
+}
+
+void GfxDevice::BeginFrameStats()
+{
+ m_Stats.BeginFrameStats();
+}
+
+void GfxDevice::EndFrameStats()
+{
+ m_Stats.EndClientFrameStats();
+}
+
+void GfxDevice::SaveDrawStats()
+{
+ m_SavedStats.CopyAllDrawStats(m_Stats);
+ m_SavedStats.CopyClientStats(m_Stats);
+}
+
+void GfxDevice::RestoreDrawStats()
+{
+ m_Stats.CopyAllDrawStats(m_SavedStats);
+ m_Stats.CopyClientStats(m_SavedStats);
+}
+
+void GfxDevice::SynchronizeStats()
+{
+}
+
+#if UNITY_EDITOR
+void GfxDevice::SetColorBytes (const UInt8 color[4])
+{
+ float colorFloat[4];
+ colorFloat[0] = ByteToNormalized(color[0]);
+ colorFloat[1] = ByteToNormalized(color[1]);
+ colorFloat[2] = ByteToNormalized(color[2]);
+ colorFloat[3] = ByteToNormalized(color[3]);
+ SetColor (colorFloat);
+}
+#endif
+
+
+void GfxThreadableDevice::SetShadersMainThread (ShaderLab::SubProgram* programs[kShaderTypeCount], const ShaderLab::PropertySheet* props)
+{
+ ErrorString("Don't call SetShadersMainThread on threadable device! Use GraphicsHelper instead");
+}
+
+
+
+void GfxDevice::CommonReloadResources(UInt32 flags)
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS_WORKER
+ if (flags & kReloadTextures)
+ Texture::ReloadAll();
+
+ if (flags & kReloadShaders)
+ Shader::ReloadAllShaders();
+
+ if (flags & kReleaseRenderTextures)
+ RenderTexture::ReleaseAll();
+#else
+ //todo.
+#endif
+}
+
+
+void CalculateDeviceProjectionMatrix (Matrix4x4f& m, bool usesOpenGLTextureCoords, bool invertY)
+{
+ if (usesOpenGLTextureCoords)
+ return; // nothing to do on OpenGL-like devices
+
+ // Otherwise, the matrix is OpenGL style, and we have to convert it to
+ // D3D-like projection matrix
+
+ if (invertY)
+ {
+ m.Get(1,0) = -m.Get(1,0);
+ m.Get(1,1) = -m.Get(1,1);
+ m.Get(1,2) = -m.Get(1,2);
+ m.Get(1,3) = -m.Get(1,3);
+ }
+
+
+ // Now scale&bias to get Z range from -1..1 to 0..1:
+ // matrix = scaleBias * matrix
+ // 1 0 0 0
+ // 0 1 0 0
+ // 0 0 0.5 0.5
+ // 0 0 0 1
+ m.Get(2,0) = m.Get(2,0) * 0.5f + m.Get(3,0) * 0.5f;
+ m.Get(2,1) = m.Get(2,1) * 0.5f + m.Get(3,1) * 0.5f;
+ m.Get(2,2) = m.Get(2,2) * 0.5f + m.Get(3,2) * 0.5f;
+ m.Get(2,3) = m.Get(2,3) * 0.5f + m.Get(3,3) * 0.5f;
+}
+
+
+
+void GfxDevice::SetupVertexLightParams(int light, const GfxVertexLight& data)
+{
+ DebugAssert(light >= 0 && light < kMaxSupportedVertexLights);
+
+ const Matrix4x4f& viewMat = m_BuiltinParamValues.GetMatrixParam(kShaderMatView);
+
+ Vector4f& position = m_BuiltinParamValues.GetWritableVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Position + light));
+ Vector4f& spotDirection = m_BuiltinParamValues.GetWritableVectorParam(BuiltinShaderVectorParam(kShaderVecLight0SpotDirection + light));
+ Vector4f& atten = m_BuiltinParamValues.GetWritableVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Atten + light));
+
+ // color
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Diffuse + light), data.color);
+
+ // position
+ if (data.type == kLightDirectional)
+ {
+ Vector3f p = viewMat.MultiplyVector3 ((const Vector3f&)data.position);
+ position.Set(-p.x, -p.y, -p.z, 0.0f);
+ }
+ else
+ {
+ Vector3f p = viewMat.MultiplyPoint3 ((const Vector3f&)data.position);
+ position.Set(p.x, p.y, p.z, 1.0f);
+ }
+
+ // attenuation set in a way where distance attenuation can be computed:
+ // float lengthSq = dot(toLight, toLight);
+ // float atten = 1.0 / (1.0 + lengthSq * unity_LightAtten[i].z);
+ // and spot cone attenuation:
+ // float rho = max (0, dot(normalize(toLight), unity_SpotDirection[i].xyz));
+ // float spotAtt = (rho - unity_LightAtten[i].x) * unity_LightAtten[i].y;
+ // spotAtt = saturate(spotAtt);
+ // and the above works for all light types, i.e. spot light code works out
+ // to correct math for point & directional lights as well.
+
+ const float rangeSq = data.range * data.range;
+
+ // spot direction & attenuation
+ if (data.spotAngle > 0.0f)
+ {
+ // spot light
+ Vector3f d = viewMat.MultiplyVector3((const Vector3f&)data.spotDirection);
+ spotDirection.Set(-d.x, -d.y, -d.z, 0.0f);
+
+ const float radAngle = Deg2Rad(data.spotAngle);
+ const float cosTheta = cosf(radAngle*0.25f);
+ const float cosPhi = cosf(radAngle*0.5f);
+ const float cosDiff = cosTheta - cosPhi;
+ atten.Set(cosPhi, (cosDiff != 0.0f) ? 1.0f / cosDiff : 1.0f, data.quadAtten, rangeSq);
+ }
+ else
+ {
+ // non-spot light
+ spotDirection.Set(0.0f, 0.0f, 1.0f, 0.0f);
+ atten.Set(-1.0f, 1.0f, data.quadAtten, rangeSq);
+ }
+}
+
+
+
+#if UNITY_EDITOR
+VertexComponent kSuitableVertexComponentForChannel[kShaderChannelCount] = {
+ kVertexCompVertex,
+ kVertexCompNormal,
+ kVertexCompColor,
+ kVertexCompTexCoord0,
+ kVertexCompTexCoord1,
+ kVertexCompTexCoord2,
+};
+#endif
+
+static const float kDodecahedron[20][3] = {
+ { 0.607f, 0.000f, 0.795f },
+ { 0.188f, 0.577f, 0.795f },
+ { -0.491f, 0.357f, 0.795f },
+ { -0.491f, -0.357f, 0.795f },
+ { 0.188f, -0.577f, 0.795f },
+ { 0.982f, 0.000f, 0.188f },
+ { 0.304f, 0.934f, 0.188f },
+ { -0.795f, 0.577f, 0.188f },
+ { -0.795f, -0.577f, 0.188f },
+ { 0.304f, -0.934f, 0.188f },
+ { 0.795f, 0.577f, -0.188f },
+ { -0.304f, 0.934f, -0.188f },
+ { -0.982f, 0.000f, -0.188f },
+ { -0.304f, -0.934f, -0.188f },
+ { 0.795f, -0.577f, -0.188f },
+ { 0.491f, 0.357f, -0.795f },
+ { -0.188f, 0.577f, -0.795f },
+ { -0.607f, 0.000f, -0.795f },
+ { -0.188f, -0.577f, -0.795f },
+ { 0.491f, -0.357f, -0.795f },
+};
+
+#define DODECAHEDRON_TRIANGLE(x,y,z,a,b,c,s) \
+ ImmediateVertex(x + kDodecahedron[a][0] * s, y + kDodecahedron[a][1] * s, z + kDodecahedron[a][2] * s); \
+ ImmediateVertex(x + kDodecahedron[b][0] * s, y + kDodecahedron[b][1] * s, z + kDodecahedron[b][2] * s); \
+ ImmediateVertex(x + kDodecahedron[c][0] * s, y + kDodecahedron[c][1] * s, z + kDodecahedron[c][2] * s);
+
+#define DODECAHEDRON_FACE(x,y,z,a,b,c,d,e,s) \
+ DODECAHEDRON_TRIANGLE(x,y,z,a,b,c,s) \
+ DODECAHEDRON_TRIANGLE(x,y,z,a,c,d,s) \
+ DODECAHEDRON_TRIANGLE(x,y,z,a,d,e,s)
+
+void GfxDevice::ImmediateShape( float x, float y, float z, float scale, ImmediateShapeType shape )
+{
+ switch (shape)
+ {
+ case kShapeCube:
+ ImmediateBegin(kPrimitiveQuads);
+ ImmediateNormal(0, 0, 0);
+ // -z
+ ImmediateVertex (x+scale, y-scale, z-scale);
+ ImmediateVertex (x-scale, y-scale, z-scale);
+ ImmediateVertex (x-scale, y+scale, z-scale);
+ ImmediateVertex (x+scale, y+scale, z-scale);
+ // +z
+ ImmediateVertex (x-scale, y-scale, z+scale);
+ ImmediateVertex (x+scale, y-scale, z+scale);
+ ImmediateVertex (x+scale, y+scale, z+scale);
+ ImmediateVertex (x-scale, y+scale, z+scale);
+ // -x
+ ImmediateVertex (x-scale, y+scale, z-scale);
+ ImmediateVertex (x-scale, y-scale, z-scale);
+ ImmediateVertex (x-scale, y-scale, z+scale);
+ ImmediateVertex (x-scale, y+scale, z+scale);
+ // +x
+ ImmediateVertex (x+scale, y-scale, z-scale);
+ ImmediateVertex (x+scale, y+scale, z-scale);
+ ImmediateVertex (x+scale, y+scale, z+scale);
+ ImmediateVertex (x+scale, y-scale, z+scale);
+ // -y
+ ImmediateVertex (x-scale, y-scale, z-scale);
+ ImmediateVertex (x+scale, y-scale, z-scale);
+ ImmediateVertex (x+scale, y-scale, z+scale);
+ ImmediateVertex (x-scale, y-scale, z+scale);
+ // +y
+ ImmediateVertex (x+scale, y+scale, z-scale);
+ ImmediateVertex (x-scale, y+scale, z-scale);
+ ImmediateVertex (x-scale, y+scale, z+scale);
+ ImmediateVertex (x+scale, y+scale, z+scale);
+
+ ImmediateEnd();
+ break;
+
+ case kShapeDodecahedron:
+ // template edge length
+ // a = 0.713644
+ // radius of sphere containing the dodecahedron
+ // r = a / 20 * sqrtf(250 + 110*sqrtf(5))
+ // scale our radius to fit the template
+ // TODO: is this correct? :)
+ scale = scale * 1.258408f;
+
+ ImmediateBegin(kPrimitiveTriangles);
+ ImmediateNormal(0, 0, 0);
+
+ DODECAHEDRON_FACE(x,y,z, 0,1,2,3,4, scale);
+ DODECAHEDRON_FACE(x,y,z, 0,5,10,6,1, scale);
+ DODECAHEDRON_FACE(x,y,z, 1,6,11,7,2, scale);
+ DODECAHEDRON_FACE(x,y,z, 2,7,12,8,3, scale);
+ DODECAHEDRON_FACE(x,y,z, 3,8,13,9,4, scale);
+ DODECAHEDRON_FACE(x,y,z, 4,9,14,5,0, scale);
+ DODECAHEDRON_FACE(x,y,z, 15,16,11,6,10, scale);
+ DODECAHEDRON_FACE(x,y,z, 16,17,12,7,11, scale);
+ DODECAHEDRON_FACE(x,y,z, 17,18,13,8,12, scale);
+ DODECAHEDRON_FACE(x,y,z, 18,19,14,9,13, scale);
+ DODECAHEDRON_FACE(x,y,z, 19,15,10,5,14, scale);
+ DODECAHEDRON_FACE(x,y,z, 15,19,18,17,16, scale);
+
+ ImmediateEnd();
+ break;
+
+ default:
+ FatalErrorString("Unknown ImmediateShape");
+ break;
+ };
+}
+
+#undef DODECAHEDRON_FACE
+#undef DODECAHEDRON_TRIANGLE
+
+UInt32 GfxDevice::GetNativeTextureID(TextureID id)
+{
+#if ENABLE_TEXTUREID_MAP
+ return TextureIdMap::QueryNativeTexture(id);
+#else
+ return id.m_ID;
+#endif
+}
+
+void GfxDevice::InsertCustomMarker (int marker)
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS_WORKER
+ PluginsRenderMarker (marker);
+#endif
+}