summaryrefslogtreecommitdiff
path: root/Runtime/Shaders
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Shaders')
-rw-r--r--Runtime/Shaders/BufferedVBO.cpp142
-rw-r--r--Runtime/Shaders/BufferedVBO.h35
-rw-r--r--Runtime/Shaders/ComputeShader.cpp425
-rw-r--r--Runtime/Shaders/ComputeShader.h164
-rw-r--r--Runtime/Shaders/GraphicsCaps.cpp610
-rw-r--r--Runtime/Shaders/GraphicsCaps.h435
-rw-r--r--Runtime/Shaders/Material.cpp1096
-rw-r--r--Runtime/Shaders/Material.h233
-rw-r--r--Runtime/Shaders/MaterialIsTransparent.h14
-rw-r--r--Runtime/Shaders/MaterialProperties.cpp195
-rw-r--r--Runtime/Shaders/MaterialProperties.h85
-rw-r--r--Runtime/Shaders/NameToObjectMap.h153
-rw-r--r--Runtime/Shaders/Shader.cpp734
-rw-r--r--Runtime/Shaders/Shader.h174
-rw-r--r--Runtime/Shaders/ShaderKeywords.cpp90
-rw-r--r--Runtime/Shaders/ShaderKeywords.h43
-rw-r--r--Runtime/Shaders/ShaderNameRegistry.cpp235
-rw-r--r--Runtime/Shaders/ShaderNameRegistry.h54
-rw-r--r--Runtime/Shaders/ShaderSupportScripts.cpp19
-rw-r--r--Runtime/Shaders/ShaderSupportScripts.h26
-rw-r--r--Runtime/Shaders/ShaderTags.h27
-rw-r--r--Runtime/Shaders/ShaderTests.cpp285
-rw-r--r--Runtime/Shaders/UnityPropertySheet.cpp144
-rw-r--r--Runtime/Shaders/UnityPropertySheet.h67
-rw-r--r--Runtime/Shaders/VBO.cpp170
-rw-r--r--Runtime/Shaders/VBO.h308
26 files changed, 5963 insertions, 0 deletions
diff --git a/Runtime/Shaders/BufferedVBO.cpp b/Runtime/Shaders/BufferedVBO.cpp
new file mode 100644
index 0000000..452d993
--- /dev/null
+++ b/Runtime/Shaders/BufferedVBO.cpp
@@ -0,0 +1,142 @@
+#include "UnityPrefix.h"
+#include "BufferedVBO.h"
+
+
+BufferedVBO::BufferedVBO()
+{
+ m_AllocatedSize = 0;
+}
+
+BufferedVBO::~BufferedVBO()
+{
+ UnbufferVertexData();
+}
+
+void BufferedVBO::BufferAllVertexData( const VertexBufferData& buffer )
+{
+ bool copyModes[kStreamModeCount];
+ std::fill(copyModes, copyModes + kStreamModeCount, true);
+ BufferVertexData(buffer, copyModes);
+}
+
+void BufferedVBO::BufferAccessibleVertexData( const VertexBufferData& buffer )
+{
+ bool copyModes[kStreamModeCount];
+ std::fill(copyModes, copyModes + kStreamModeCount, true);
+ copyModes[kStreamModeNoAccess] = false;
+ BufferVertexData(buffer, copyModes);
+}
+
+void BufferedVBO::BufferVertexData( const VertexBufferData& buffer, bool copyModes[kStreamModeCount] )
+{
+ std::copy(buffer.channels, buffer.channels + kShaderChannelCount, m_VertexData.channels);
+
+ int streamOffset = 0;
+ for (int s = 0; s < kMaxVertexStreams; s++)
+ {
+ StreamInfo& stream = m_VertexData.streams[s];
+ const StreamInfo& srcStream = buffer.streams[s];
+ UInt8 mode = m_StreamModes[s];
+ Assert(mode < kStreamModeCount);
+ if (copyModes[mode] && srcStream.channelMask)
+ {
+ stream = srcStream;
+ stream.offset = streamOffset;
+ streamOffset += CalculateVertexStreamSize(buffer, s);
+ streamOffset = VertexData::AlignStreamSize(streamOffset);
+ }
+ else
+ stream.Reset();
+ }
+ size_t allocSize = VertexData::GetAllocateDataSize(streamOffset);
+ if (allocSize != m_AllocatedSize)
+ {
+ UnbufferVertexData();
+ m_AllocatedSize = allocSize;
+ m_VertexData.buffer = (UInt8*)UNITY_MALLOC_ALIGNED (kMemVertexData, allocSize, VertexData::kVertexDataAlign);
+ }
+ m_VertexData.bufferSize = streamOffset;
+ m_VertexData.vertexCount = buffer.vertexCount;
+
+ for (int s = 0; s < kMaxVertexStreams; s++)
+ {
+ const StreamInfo& srcStream = buffer.streams[s];
+ UInt8 mode = m_StreamModes[s];
+ if (copyModes[mode] && srcStream.channelMask)
+ CopyVertexStream(buffer, GetStreamBuffer(s), s);
+ }
+}
+
+void BufferedVBO::UnbufferVertexData()
+{
+ if (m_AllocatedSize > 0)
+ UNITY_FREE(kMemVertexData, m_VertexData.buffer);
+
+ m_AllocatedSize = 0;
+ m_VertexData.buffer = NULL;
+}
+
+bool BufferedVBO::MapVertexStream( VertexStreamData& outData, unsigned stream )
+{
+ Assert(!m_IsStreamMapped[stream]);
+ DebugAssert(m_StreamModes[stream] != kStreamModeNoAccess);
+ if (m_StreamModes[stream] == kStreamModeNoAccess)
+ return false;
+ DebugAssert(m_VertexData.buffer);
+ if (!m_VertexData.buffer)
+ return false;
+ outData.buffer = m_VertexData.buffer + m_VertexData.streams[stream].offset;
+ outData.channelMask = m_VertexData.streams[stream].channelMask;
+ outData.stride = m_VertexData.streams[stream].stride;
+ outData.vertexCount = m_VertexData.vertexCount;
+ m_IsStreamMapped[stream] = true;
+ return true;
+}
+
+void BufferedVBO::UnmapVertexStream( unsigned stream )
+{
+ Assert(m_IsStreamMapped[stream]);
+ m_IsStreamMapped[stream] = false;
+}
+
+int BufferedVBO::GetRuntimeMemorySize() const
+{
+ return m_AllocatedSize;
+}
+
+UInt8* BufferedVBO::GetStreamBuffer(unsigned stream)
+{
+ return m_VertexData.buffer + m_VertexData.streams[stream].offset;
+}
+
+UInt8* BufferedVBO::GetChannelDataAndStride(ShaderChannel channel, UInt32& outStride)
+{
+ const ChannelInfo& info = m_VertexData.channels[channel];
+ if (info.IsValid())
+ {
+ outStride = info.CalcStride(m_VertexData.streams);
+ return m_VertexData.buffer + info.CalcOffset(m_VertexData.streams);
+ }
+ outStride = 0;
+ return NULL;
+}
+
+void BufferedVBO::GetChannelDataAndStrides(void* channelData[kShaderChannelCount], UInt32 outStrides[kShaderChannelCount])
+{
+ for (int i = 0; i < kShaderChannelCount; i++)
+ channelData[i] = GetChannelDataAndStride(ShaderChannel(i), outStrides[i]);
+}
+
+void BufferedVBO::GetChannelOffsetsAndStrides(void* channelOffsets[kShaderChannelCount], UInt32 outStrides[kShaderChannelCount])
+{
+ for (int i = 0; i < kShaderChannelCount; i++)
+ {
+ channelOffsets[i] = reinterpret_cast<void*>(m_VertexData.channels[i].CalcOffset(m_VertexData.streams));
+ outStrides[i] = m_VertexData.channels[i].CalcStride(m_VertexData.streams);
+ }
+}
+
+void BufferedVBO::UnloadSourceVertices()
+{
+ UnbufferVertexData();
+}
diff --git a/Runtime/Shaders/BufferedVBO.h b/Runtime/Shaders/BufferedVBO.h
new file mode 100644
index 0000000..5932b28
--- /dev/null
+++ b/Runtime/Shaders/BufferedVBO.h
@@ -0,0 +1,35 @@
+#ifndef BUFFEREDVBO_H
+#define BUFFEREDVBO_H
+
+#include "VBO.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+class BufferedVBO : public VBO
+{
+public:
+ BufferedVBO();
+ ~BufferedVBO();
+
+ virtual bool MapVertexStream( VertexStreamData& outData, unsigned stream );
+ virtual void UnmapVertexStream( unsigned stream );
+
+ virtual int GetRuntimeMemorySize() const;
+
+ virtual void UnloadSourceVertices();
+
+protected:
+ void BufferAllVertexData( const VertexBufferData& buffer );
+ void BufferAccessibleVertexData( const VertexBufferData& buffer );
+ void BufferVertexData( const VertexBufferData& buffer, bool copyModes[kStreamModeCount] );
+ void UnbufferVertexData();
+
+ UInt8* GetStreamBuffer(unsigned stream);
+ UInt8* GetChannelDataAndStride(ShaderChannel channel, UInt32& outStride);
+ void GetChannelDataAndStrides(void* channelData[kShaderChannelCount], UInt32 outStrides[kShaderChannelCount]);
+ void GetChannelOffsetsAndStrides(void* channelOffsets[kShaderChannelCount], UInt32 outStrides[kShaderChannelCount]);
+
+ VertexBufferData m_VertexData;
+ size_t m_AllocatedSize;
+};
+
+#endif
diff --git a/Runtime/Shaders/ComputeShader.cpp b/Runtime/Shaders/ComputeShader.cpp
new file mode 100644
index 0000000..f5b9f40
--- /dev/null
+++ b/Runtime/Shaders/ComputeShader.cpp
@@ -0,0 +1,425 @@
+#include "UnityPrefix.h"
+#include "ComputeShader.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Profiler/Profiler.h"
+
+PROFILER_INFORMATION(gDispatchComputeProfile, "Compute.Dispatch", kProfilerRender)
+
+
+// --------------------------------------------------------------------------
+
+
+struct ComputeProgramStruct
+{
+ ComputeProgramHandle handle;
+ int cbBindPoints[kMaxSupportedConstantBuffers];
+ int texBindPoints[kMaxSupportedComputeResources];
+ TextureID textures[kMaxSupportedComputeResources];
+ unsigned builtinSamplers[kMaxSupportedComputeResources]; // highest 16 bits - builtin sampler type; lowest 16 bits - bind point
+ int inBufBindPoints[kMaxSupportedComputeResources];
+ ComputeBufferID inBuffers[kMaxSupportedComputeResources];
+ UInt32 outBufBindPoints[kMaxSupportedComputeResources]; // highest bit indicates whether this is raw UAV or a texture UAV
+ ComputeBufferID outBuffers[kMaxSupportedComputeResources];
+ TextureID outTextures[kMaxSupportedComputeResources];
+};
+
+
+ComputeShader::ComputeShader(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_Programs(NULL)
+, m_ProgramCount(0)
+, m_DataBuffer(NULL)
+, m_DataBufferSize(0)
+, m_CBDirty(0)
+{
+}
+
+ComputeShader::~ComputeShader ()
+{
+ DestroyRuntimeData ();
+}
+
+
+void ComputeShader::DestroyRuntimeData ()
+{
+ GfxDevice& device = GetGfxDevice();
+ for (int i = 0; i < m_ProgramCount; ++i)
+ {
+ device.DestroyComputeProgram (m_Programs[i].handle);
+ }
+ delete[] m_Programs;
+ m_Programs = NULL;
+ m_ProgramCount = 0;
+
+ device.DestroyComputeConstantBuffers (m_ConstantBuffers.size(), m_CBs);
+
+ delete[] m_DataBuffer;
+ m_DataBuffer = NULL;
+ m_DataBufferSize = 0;
+ m_CBDirty = 0;
+}
+
+void ComputeShader::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ DestroyRuntimeData ();
+ CreateRuntimeData();
+}
+
+void ComputeShader::CreateRuntimeData()
+{
+ // create compute programs
+ GfxDevice& device = GetGfxDevice();
+ m_ProgramCount = m_Kernels.size();
+ m_Programs = new ComputeProgramStruct[m_ProgramCount];
+ for (int i = 0; i < m_ProgramCount; ++i)
+ {
+ const ComputeShaderKernel& kernelData = m_Kernels[i];
+ m_Programs[i].handle = device.CreateComputeProgram (kernelData.code.data(), kernelData.code.size());
+
+ // compute CS bind points for this kernel
+ memset (m_Programs[i].cbBindPoints, -1, sizeof(m_Programs[i].cbBindPoints));
+ for (size_t r = 0, nr = kernelData.cbs.size(); r < nr; ++r)
+ {
+ const ComputeShaderResource& res = kernelData.cbs[r];
+ for (size_t cb = 0, ncb = m_ConstantBuffers.size(); cb < ncb; ++cb)
+ {
+ if (m_ConstantBuffers[cb].name == res.name)
+ {
+ m_Programs[i].cbBindPoints[cb] = res.bindPoint;
+ break;
+ }
+ }
+ }
+
+ for (size_t t = 0, nt = kernelData.textures.size(); t < nt; ++t)
+ {
+ m_Programs[i].texBindPoints[t] = kernelData.textures[t].bindPoint;
+ m_Programs[i].textures[t].m_ID = 0;
+ }
+ for (size_t s = 0, ns = kernelData.builtinSamplers.size(); s < ns; ++s)
+ {
+ m_Programs[i].builtinSamplers[s] = (kernelData.builtinSamplers[s].sampler << 16) | (kernelData.builtinSamplers[s].bindPoint);
+ }
+ for (size_t b = 0, nb = kernelData.inBuffers.size(); b < nb; ++b)
+ {
+ m_Programs[i].inBufBindPoints[b] = kernelData.inBuffers[b].bindPoint;
+ m_Programs[i].inBuffers[b] = ComputeBufferID();
+ }
+ for (size_t b = 0, nb = kernelData.outBuffers.size(); b < nb; ++b)
+ {
+ m_Programs[i].outBufBindPoints[b] = kernelData.outBuffers[b].bindPoint;
+ m_Programs[i].outBuffers[b] = ComputeBufferID();
+ m_Programs[i].outTextures[b].m_ID = 0;
+ }
+ }
+
+ // calculate space to hold all CBs, and offsets into the buffer
+ m_CBDirty = 0;
+ m_DataBufferSize = 0;
+ for (int cb = 0; cb < m_ConstantBuffers.size(); ++cb)
+ {
+ m_CBSizes[cb] = m_ConstantBuffers[cb].byteSize;
+ m_CBOffsets[cb] = m_DataBufferSize;
+ m_DataBufferSize += m_ConstantBuffers[cb].byteSize;
+ }
+ m_DataBuffer = new UInt8[m_DataBufferSize];
+ memset (m_DataBuffer, 0, m_DataBufferSize);
+
+ // create constant buffers
+ device.CreateComputeConstantBuffers (m_ConstantBuffers.size(), m_CBSizes, m_CBs);
+}
+
+
+int ComputeShader::FindKernel (const FastPropertyName& name) const
+{
+ for (size_t i = 0, n = m_Kernels.size(); i < n; ++i)
+ {
+ if (m_Kernels[i].name == name)
+ return i;
+ }
+ return -1;
+}
+
+
+void ComputeShader::SetValueParam (const FastPropertyName& name, int byteCount, const void* data)
+{
+ for (size_t icb = 0, ncb = m_ConstantBuffers.size(); icb < ncb; ++icb)
+ {
+ const ComputeShaderCB& cb = m_ConstantBuffers[icb];
+ for (size_t p = 0, np = cb.params.size(); p < np; ++p)
+ {
+ if (cb.params[p].name == name)
+ {
+ const int cbOffset = cb.params[p].offset;
+ const int cbByteSize = cb.byteSize;
+ if (cbOffset >= cbByteSize || cbOffset + byteCount > cbByteSize)
+ return;
+
+ m_CBDirty |= (1<<icb);
+ memcpy (m_DataBuffer + m_CBOffsets[icb] + cbOffset, data, byteCount);
+ }
+ }
+ }
+}
+
+
+void ComputeShader::SetTextureParam (unsigned kernelIdx, const FastPropertyName& name, TextureID tid)
+{
+ if (kernelIdx >= m_ProgramCount)
+ return;
+
+ const std::vector<ComputeShaderResource>& textures = m_Kernels[kernelIdx].textures;
+ for (size_t i = 0, n = textures.size(); i < n; ++i)
+ {
+ if (textures[i].name == name)
+ {
+ m_Programs[kernelIdx].textures[i] = tid;
+ break;
+ }
+ }
+
+ const std::vector<ComputeShaderResource>& outBuffers = m_Kernels[kernelIdx].outBuffers;
+ for (size_t i = 0, n = outBuffers.size(); i < n; ++i)
+ {
+ if (outBuffers[i].name == name)
+ {
+ m_Programs[kernelIdx].outTextures[i] = tid;
+ m_Programs[kernelIdx].outBufBindPoints[i] |= 0x80000000; // set highest bit, indicating it's a texture param
+ break;
+ }
+ }
+}
+
+void ComputeShader::SetBufferParam (unsigned kernelIdx, const FastPropertyName& name, ComputeBufferID handle)
+{
+ if (kernelIdx >= m_ProgramCount)
+ return;
+
+ const std::vector<ComputeShaderResource>& inBuffers = m_Kernels[kernelIdx].inBuffers;
+ for (size_t i = 0, n = inBuffers.size(); i < n; ++i)
+ {
+ if (inBuffers[i].name == name)
+ {
+ m_Programs[kernelIdx].inBuffers[i] = handle;
+ break;
+ }
+ }
+
+ const std::vector<ComputeShaderResource>& outBuffers = m_Kernels[kernelIdx].outBuffers;
+ for (size_t i = 0, n = outBuffers.size(); i < n; ++i)
+ {
+ if (outBuffers[i].name == name)
+ {
+ m_Programs[kernelIdx].outBuffers[i] = handle;
+ m_Programs[kernelIdx].outBufBindPoints[i] &= 0x7FFFFFFF; // clear highest bit, indicating it's a buffer param
+ break;
+ }
+ }
+}
+
+
+void ComputeShader::DispatchComputeShader (unsigned kernelIdx, int threadsX, int threadsY, int threadsZ)
+{
+ if (!gGraphicsCaps.hasComputeShader)
+ {
+ ErrorString ("Platform does not support compute shaders");
+ return;
+ }
+
+ if (kernelIdx >= m_ProgramCount)
+ return;
+
+ GPU_AUTO_SECTION(kGPUSectionOther);
+ PROFILER_AUTO(gDispatchComputeProfile, this)
+
+ GfxDevice& device = GetGfxDevice();
+ const unsigned cbCount = m_ConstantBuffers.size();
+ device.UpdateComputeConstantBuffers (cbCount, m_CBs, m_CBDirty, m_DataBufferSize, m_DataBuffer, m_CBSizes, m_CBOffsets, m_Programs[kernelIdx].cbBindPoints);
+ device.UpdateComputeResources (
+ m_Kernels[kernelIdx].textures.size(), m_Programs[kernelIdx].textures, m_Programs[kernelIdx].texBindPoints,
+ m_Kernels[kernelIdx].builtinSamplers.size(), m_Programs[kernelIdx].builtinSamplers,
+ m_Kernels[kernelIdx].inBuffers.size(), m_Programs[kernelIdx].inBuffers, m_Programs[kernelIdx].inBufBindPoints,
+ m_Kernels[kernelIdx].outBuffers.size(), m_Programs[kernelIdx].outBuffers, m_Programs[kernelIdx].outTextures, m_Programs[kernelIdx].outBufBindPoints
+ );
+ device.DispatchComputeProgram (m_Programs[kernelIdx].handle, threadsX, threadsY, threadsZ);
+ GPU_TIMESTAMP();
+
+ // CBs we have just used aren't dirty anymore
+ for (unsigned i = 0; i < cbCount; ++i)
+ {
+ if (m_Programs[kernelIdx].cbBindPoints[i] >= 0)
+ {
+ UInt32 dirtyMask = (1<<i);
+ m_CBDirty &= ~dirtyMask;
+ }
+ }
+}
+
+
+
+template<class TransferFunc>
+void ComputeShaderParam::Transfer (TransferFunc& transfer)
+{
+ TRANSFER(name);
+ TRANSFER_ENUM(type)
+ TRANSFER(offset);
+ TRANSFER(arraySize);
+ TRANSFER(rowCount);
+ TRANSFER(colCount);
+}
+
+template<class TransferFunc>
+void ComputeShaderCB::Transfer (TransferFunc& transfer)
+{
+ TRANSFER(name);
+ TRANSFER(byteSize);
+ TRANSFER(params);
+}
+
+template<class TransferFunc>
+void ComputeShaderResource::Transfer (TransferFunc& transfer)
+{
+ TRANSFER(name);
+ TRANSFER(bindPoint);
+}
+
+template<class TransferFunc>
+void ComputeShaderBuiltinSampler::Transfer (TransferFunc& transfer)
+{
+ transfer.Transfer((int&)sampler, "sampler");
+ TRANSFER(bindPoint);
+}
+
+template<class TransferFunc>
+void ComputeShaderKernel::Transfer (TransferFunc& transfer)
+{
+ TRANSFER(name);
+ TRANSFER(cbs);
+ TRANSFER(textures);
+ TRANSFER(builtinSamplers);
+ TRANSFER(inBuffers);
+ TRANSFER(outBuffers);
+ transfer.Transfer(code, "code", kHideInEditorMask);
+}
+
+
+template<class TransferFunc>
+void ComputeShader::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+
+ transfer.Transfer (m_Kernels, "kernels");
+ transfer.Transfer (m_ConstantBuffers, "constantBuffers");
+
+ #if UNITY_EDITOR
+ if (!transfer.IsSerializingForGameRelease())
+ transfer.Transfer (m_Errors.GetErrors(), "errors");
+ #endif
+}
+
+
+IMPLEMENT_CLASS (ComputeShader)
+IMPLEMENT_OBJECT_SERIALIZE (ComputeShader)
+
+
+// --------------------------------------------------------------------------
+
+
+typedef List< ListNode<ComputeBuffer> > ComputeBufferList;
+static ComputeBufferList s_ComputeBuffers;
+
+
+ComputeBuffer::ComputeBuffer (size_t count, size_t stride, UInt32 flags)
+: m_Count(count)
+, m_Stride(stride)
+, m_Flags(flags)
+, m_ComputeBuffersNode(this)
+{
+ s_ComputeBuffers.push_back(m_ComputeBuffersNode);
+ ReloadToGfxDevice();
+}
+
+ComputeBuffer::~ComputeBuffer ()
+{
+ UnloadFromGfxDevice();
+ m_ComputeBuffersNode.RemoveFromList();
+}
+
+void ComputeBuffer::UnloadFromGfxDevice()
+{
+ GfxDevice& device = GetGfxDevice();
+ device.DestroyComputeBuffer (m_BufferHandle);
+ device.FreeComputeBufferID (m_BufferHandle);
+ m_BufferHandle.m_ID = 0;
+}
+
+void ComputeBuffer::ReloadToGfxDevice()
+{
+ GfxDevice& device = GetGfxDevice();
+ m_BufferHandle = device.CreateComputeBufferID();
+ device.CreateComputeBuffer (m_BufferHandle, m_Count, m_Stride, m_Flags);
+}
+
+
+static size_t ValidateSizeAgainstBufferAndStride (size_t size, size_t bufferSize, size_t stride)
+{
+ if (0 == stride)
+ return 0;
+
+ size = std::min (size, bufferSize);
+ size /= stride;
+ size *= stride;
+ return size;
+}
+
+void ComputeBuffer::SetData (const void* data, size_t size)
+{
+ if (!data || !size || !m_BufferHandle.IsValid())
+ return;
+
+ // make sure size is not too large and multiple of stride
+ size = ValidateSizeAgainstBufferAndStride (size, m_Count * m_Stride, m_Stride);
+ GetGfxDevice().SetComputeBufferData (m_BufferHandle, data, size);
+}
+
+
+void ComputeBuffer::GetData (void* dest, size_t destSize)
+{
+ if (!dest || !destSize || !m_BufferHandle.IsValid())
+ return;
+
+ // make sure size is not too large and multiple of stride
+ destSize = ValidateSizeAgainstBufferAndStride (destSize, m_Count * m_Stride, m_Stride);
+ GetGfxDevice().GetComputeBufferData (m_BufferHandle, dest, destSize);
+}
+
+
+
+void ComputeBuffer::CopyCount (ComputeBuffer* src, ComputeBuffer* dst, UInt32 dstOffset)
+{
+ if (!src || !src->m_BufferHandle.IsValid())
+ return;
+ if (!dst || !dst->m_BufferHandle.IsValid())
+ return;
+ UInt32 srcTypeFlags = src->m_Flags & kCBFlagTypeMask;
+ if (!(srcTypeFlags & kCBFlagAppend) && !(srcTypeFlags & kCBFlagCounter))
+ return;
+
+ GetGfxDevice().CopyComputeBufferCount (src->m_BufferHandle, dst->m_BufferHandle, dstOffset);
+}
+
+
+void ComputeBuffer::UnloadAllFromGfxDevice ()
+{
+ for (ComputeBufferList::iterator i = s_ComputeBuffers.begin(); i != s_ComputeBuffers.end(); ++i)
+ (**i).UnloadFromGfxDevice();
+}
+
+void ComputeBuffer::ReloadAllToGfxDevice ()
+{
+ for (ComputeBufferList::iterator i = s_ComputeBuffers.begin(); i != s_ComputeBuffers.end(); ++i)
+ (**i).ReloadToGfxDevice();
+}
diff --git a/Runtime/Shaders/ComputeShader.h b/Runtime/Shaders/ComputeShader.h
new file mode 100644
index 0000000..9a91c8a
--- /dev/null
+++ b/Runtime/Shaders/ComputeShader.h
@@ -0,0 +1,164 @@
+#pragma once
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/GfxDevice/GfxDeviceObjects.h"
+#include "External/shaderlab/Library/FastPropertyName.h"
+#include "External/shaderlab/Library/ShaderLabErrors.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Utilities/NonCopyable.h"
+#include <vector>
+
+using ShaderLab::FastPropertyName;
+
+
+struct ComputeProgramStruct;
+
+
+enum ComputeShaderParamType {
+ kCSParamFloat = 0,
+ kCSParamInt = 1,
+ kCSParamUInt = 2,
+ kCSParamForce32BitEnum = 0x7FFFFFFF
+};
+
+struct ComputeShaderParam {
+ DECLARE_SERIALIZE_NO_PPTR (ComputeShaderParam)
+
+ FastPropertyName name;
+ ComputeShaderParamType type;
+ int offset;
+ int arraySize;
+ int rowCount;
+ int colCount;
+};
+
+struct ComputeShaderCB {
+ DECLARE_SERIALIZE_NO_PPTR (ComputeShaderCB)
+
+ FastPropertyName name;
+ int byteSize;
+ std::vector<ComputeShaderParam> params;
+};
+
+struct ComputeShaderResource {
+ DECLARE_SERIALIZE_NO_PPTR (ComputeShaderResource)
+ FastPropertyName name;
+ int bindPoint;
+};
+
+struct ComputeShaderBuiltinSampler {
+ DECLARE_SERIALIZE_NO_PPTR (ComputeShaderBuiltinSampler)
+ BuiltinSamplerState sampler;
+ int bindPoint;
+};
+
+struct ComputeShaderKernel {
+ DECLARE_SERIALIZE_NO_PPTR (ComputeShaderKernel)
+
+ FastPropertyName name;
+ std::vector<ComputeShaderResource> cbs;
+ // For textures, bind point is two 16 bit fields:
+ // lower 16 bits - texture bind point
+ // upper 16 bits - sampler bind point, or 0xFFFF if no sampler needed
+ std::vector<ComputeShaderResource> textures;
+ std::vector<ComputeShaderBuiltinSampler> builtinSamplers;
+ std::vector<ComputeShaderResource> inBuffers;
+ std::vector<ComputeShaderResource> outBuffers;
+ dynamic_array<UInt8> code;
+};
+
+
+
+class ComputeShader : public NamedObject {
+public:
+ typedef std::vector<ComputeShaderKernel> KernelArray;
+ typedef std::vector<ComputeShaderCB> CBArray;
+
+public:
+ REGISTER_DERIVED_CLASS (ComputeShader, NamedObject)
+ DECLARE_OBJECT_SERIALIZE (ComputeShader)
+
+ ComputeShader (MemLabelId label, ObjectCreationMode mode);
+ // ~ComputeShader (); declared-by-macro
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ KernelArray& GetKernels() { return m_Kernels; }
+ CBArray& GetConstantBuffers() { return m_ConstantBuffers; }
+
+ #if UNITY_EDITOR
+ const ShaderErrors& GetErrors() const { return m_Errors; }
+ ShaderErrors& GetErrors() { return m_Errors; }
+ #endif
+
+ int FindKernel (const FastPropertyName& name) const;
+
+ void SetValueParam (const FastPropertyName& name, int byteCount, const void* data);
+ void SetTextureParam (unsigned kernelIdx, const FastPropertyName& name, TextureID tid);
+ void SetBufferParam (unsigned kernelIdx, const FastPropertyName& name, ComputeBufferID handle);
+
+ void DispatchComputeShader (unsigned kernelIdx, int threadsX, int threadsY, int threadsZ);
+
+ void UnloadFromGfxDevice() { DestroyRuntimeData(); }
+ void ReloadToGfxDevice() { CreateRuntimeData(); }
+
+private:
+ void DestroyRuntimeData ();
+ void CreateRuntimeData ();
+
+private:
+ KernelArray m_Kernels;
+ CBArray m_ConstantBuffers;
+
+ ComputeProgramStruct* m_Programs;
+ int m_ProgramCount;
+
+ UInt8* m_DataBuffer;
+ int m_DataBufferSize;
+ UInt32 m_CBDirty; // bit per CB
+ UInt32 m_CBOffsets[kMaxSupportedConstantBuffers]; // offset of each CB into data
+ UInt32 m_CBSizes[kMaxSupportedConstantBuffers];
+ ConstantBufferHandle m_CBs[kMaxSupportedConstantBuffers];
+
+ #if UNITY_EDITOR
+ ShaderErrors m_Errors;
+ #endif
+};
+
+
+
+// --------------------------------------------------------------------------
+
+
+
+class ComputeBuffer : public NonCopyable
+{
+public:
+ ComputeBuffer (size_t count, size_t stride, UInt32 flags);
+ ~ComputeBuffer ();
+
+ ComputeBufferID GetBufferHandle() const { return m_BufferHandle; }
+ size_t GetTotalSize() const { return m_Count * m_Stride; }
+ size_t GetCount() const { return m_Count; }
+ size_t GetStride() const { return m_Stride; }
+
+ void SetData (const void* data, size_t size);
+ void GetData (void* dest, size_t destSize);
+
+ static void CopyCount (ComputeBuffer* src, ComputeBuffer* dst, UInt32 dstOffset);
+
+ static void UnloadAllFromGfxDevice();
+ static void ReloadAllToGfxDevice();
+
+private:
+ void UnloadFromGfxDevice();
+ void ReloadToGfxDevice();
+
+private:
+ size_t m_Count;
+ size_t m_Stride;
+ UInt32 m_Flags;
+ ComputeBufferID m_BufferHandle;
+ ListNode<ComputeBuffer> m_ComputeBuffersNode;
+};
diff --git a/Runtime/Shaders/GraphicsCaps.cpp b/Runtime/Shaders/GraphicsCaps.cpp
new file mode 100644
index 0000000..a2382ac
--- /dev/null
+++ b/Runtime/Shaders/GraphicsCaps.cpp
@@ -0,0 +1,610 @@
+#include "UnityPrefix.h"
+#include "GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/SystemInfo.h"
+
+GraphicsCaps gGraphicsCaps;
+
+#if UNITY_EDITOR
+
+#include "Runtime/Graphics/GraphicsHelper.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Camera/Light.h"
+#include "Editor/Platform/Interface/EditorWindows.h"
+#include "Editor/Src/EditorUserBuildSettings.h"
+#include "Editor/Src/BuildPipeline/BuildTargetPlatformSpecific.h"
+#include "Runtime/Misc/PlayerSettings.h"
+#include "Runtime/Modules/ExportModules.h"
+
+static bool gDidInitializeOriginalCaps = false;
+GraphicsCaps EXPORT_COREMODULE gOriginalCaps;
+
+void InvalidateGraphicsStateInEditorWindows(); // EditorWindow
+
+
+#if GFX_SUPPORTS_D3D9
+void InitializeCombinerCapsD3D9(); // CombinerD3D.cpp
+#endif
+
+inline bool RarelySupportedFormat(RenderTextureFormat format)
+{
+ return (format == kRTFormatARGB4444) || (format == kRTFormatARGB1555);
+}
+
+bool CheckEmulationValid( const GraphicsCaps& origCaps, GraphicsCaps& emulatedCaps )
+{
+ if (origCaps.shaderCaps < emulatedCaps.shaderCaps)
+ return false;
+ if (!origCaps.hasFixedFunction && emulatedCaps.hasFixedFunction)
+ return false;
+ for (int i = 0; i < kRTFormatCount; ++i)
+ {
+ if (!RarelySupportedFormat((RenderTextureFormat)i))
+ if (!origCaps.supportsRenderTextureFormat[i] && emulatedCaps.supportsRenderTextureFormat[i])
+ return false;
+ }
+ if (origCaps.maxTexUnits < emulatedCaps.maxTexUnits)
+ return false;
+ if (!origCaps.has3DTexture && emulatedCaps.has3DTexture)
+ return false;
+ if (origCaps.npot < emulatedCaps.npot)
+ return false;
+ if (!origCaps.hasAutoMipMapGeneration && emulatedCaps.hasAutoMipMapGeneration)
+ return false;
+ if (!origCaps.hasRenderTargetStencil && emulatedCaps.hasRenderTargetStencil)
+ return false;
+
+ #if GFX_SUPPORTS_OPENGL
+ if (GetGfxDevice().GetRenderer() == kGfxRendererOpenGL)
+ {
+ if (!origCaps.gl.hasGLSL && emulatedCaps.gl.hasGLSL)
+ return false;
+ if (!origCaps.gl.hasTextureEnvCombine3ATI && emulatedCaps.gl.hasTextureEnvCombine3ATI)
+ return false;
+ }
+ #endif
+
+ return true;
+}
+
+void GraphicsCaps::ApplyEmulationSetingsOnly( const GraphicsCaps& origCaps, const Emulation emul )
+{
+ for (int q = 0; q < kTexFormatPCCount; ++q)
+ supportsTextureFormat[q] = true;
+
+ bool glesTargetAndroid = GetBuildTargetGroup(GetEditorUserBuildSettings().GetActiveBuildTarget()) == kPlatformAndroid;
+
+ switch( emul )
+ {
+ case kEmulNone:
+ *this = origCaps;
+ break;
+ case kEmulSM3:
+ // do not set texture units; just let all SM3.0 hardware use whatever they support
+ shaderCaps = kShaderLevel3;
+ supportsRenderTextureFormat[kRTFormatDepth] = true;
+ supportsRenderTextureFormat[kRTFormatARGBHalf] = true;
+ hasComputeShader = false;
+ // GL specific
+ #if GFX_SUPPORTS_OPENGL
+ gl.hasGLSL = true;
+ #endif
+ // names
+ rendererString = "Emulated Shader Model 3.0";
+ vendorString = "Emulated";
+ #if UNITY_WIN
+ fixedVersionString = "Direct3D 9.0c [emulated]";
+ #else
+ fixedVersionString = "OpenGL 2.0 [emulated]";
+ #endif
+ break;
+ case kEmulSM2:
+ shaderCaps = kShaderLevel2;
+ supportsRenderTextureFormat[kRTFormatDepth] = true;
+ supportsRenderTextureFormat[kRTFormatARGBHalf] = false;
+ maxTexUnits = 4;
+ maxTexCoords = 8;
+ hasComputeShader = false;
+ hasInstancing = false;
+ // GL specific
+ #if GFX_SUPPORTS_OPENGL
+ gl.hasGLSL = true;
+ #endif
+ // names
+ rendererString = "Emulated Shader Model 2.0";
+ vendorString = "Emulated";
+ #if UNITY_WIN
+ fixedVersionString = "Direct3D 9.0c [emulated]";
+ #else
+ fixedVersionString = "OpenGL 2.0 [emulated]";
+ #endif
+ break;
+ case kEmulGLES20:
+ shaderCaps = kShaderLevel3;
+ //hasSRGBReadWrite = glesTargetAndroid; // while only supported on handful devices, lets allow people to use it in emu
+ hasSRGBReadWrite = false;
+ hasComputeShader = false;
+ hasInstancing = false;
+ supportsTextureFormat[kTexFormatAlphaLum16] = false;
+ supportsTextureFormat[kTexFormatDXT1] = glesTargetAndroid;
+ supportsTextureFormat[kTexFormatDXT3] = glesTargetAndroid;
+ supportsTextureFormat[kTexFormatDXT5] = glesTargetAndroid;
+ supportsTextureFormat[kTexFormatPVRTC_RGB2] = true;
+ supportsTextureFormat[kTexFormatPVRTC_RGBA2] = true;
+ supportsTextureFormat[kTexFormatPVRTC_RGB4] = true;
+ supportsTextureFormat[kTexFormatPVRTC_RGBA4] = true;
+ supportsTextureFormat[kTexFormatETC_RGB4] = true;
+ supportsTextureFormat[kTexFormatATC_RGB4] = glesTargetAndroid;
+ supportsTextureFormat[kTexFormatATC_RGBA8] = glesTargetAndroid;
+ supportsRenderTextureFormat[kRTFormatARGB32] = true;
+ supportsRenderTextureFormat[kRTFormatARGBHalf] = true; // supported on high-end mobiles ;-)
+ hasRenderToTexture = true;
+ hasTiledGPU = true; // assume tiled GPU when emulating ES2
+ warnRenderTargetUnresolves = true;
+
+ // don't touch 16bit rt formats, as they depend on underlying hw
+
+ hasNativeShadowMap = false;
+ supportsRenderTextureFormat[kRTFormatDepth] = true;
+ supportsRenderTextureFormat[kRTFormatShadowMap] = false; // will use depth texture for shadows
+
+ // There is no fixed function texture units in ES2
+ maxTexUnits = maxTexImageUnits = maxTexCoords = 8;
+ maxMRTs = 1;
+ has3DTexture = false;
+
+ // gles2.0 has hard shadows.
+ disableSoftShadows = true;
+
+ hasRenderToCubemap = true;
+ npotRT = npot = kNPOTFull;
+ maxTextureSize = 4096;
+ maxCubeMapSize = 512;
+ // GL specific
+ #if GFX_SUPPORTS_OPENGL
+ gl.hasGLSL = true;
+ gl.hasTextureEnvCombine3ATI = false;
+ gl.hasTextureEnvCombine3NV = false;
+ #endif
+ // D3D9 specific
+ #if GFX_SUPPORTS_D3D9
+ d3d.hasTextureFormatL16 = false;
+ d3d.hasTextureFormatA8L8 = false;
+ d3d.d3dcaps.MaxSimultaneousTextures = maxTexUnits;
+ d3d.d3dcaps.RasterCaps &= ~D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS;
+ #endif
+ rendererString = "Emulated GPU running OpenGL ES 2.0";
+ vendorString = "Emulated";
+ fixedVersionString = "OpenGL ES 2.0 [emulated]";
+ break;
+
+ case kEmulXBox360:
+ case kEmulPS3:
+ shaderCaps = kShaderLevel3;
+ hasFixedFunction = false;
+
+ maxLights = 8;
+ maxMRTs = 1; //@TODO: possibly needs change for MRTs on console emulation
+ hasAnisoFilter = true;
+ maxAnisoLevel = 16;
+ hasMipLevelBias = true;
+
+ hasMultiSample = (emul == kEmulPS3);
+
+ hasComputeShader = false;
+ hasInstancing = false;
+
+ hasBlendSquare = true;
+ hasSeparateAlphaBlend = true;
+
+ hasS3TCCompression = true;
+ hasVertexTextures = true;
+
+ hasAutoMipMapGeneration = false;
+
+ maxTexImageUnits = 16;
+ maxTexCoords = 8;
+
+ maxTexUnits = 8;
+
+ maxTextureSize = 4096;
+ maxCubeMapSize = 512;
+
+ supportsRenderTextureFormat[kRTFormatARGB32] = true;
+ supportsRenderTextureFormat[kRTFormatDepth] = true;
+ supportsRenderTextureFormat[kRTFormatARGBHalf] = true;
+ supportsRenderTextureFormat[kRTFormatShadowMap] = (emul == kEmulPS3);
+
+ has3DTexture = true;
+ npotRT = npot = kNPOTFull;
+
+ hasRenderToTexture = true;
+ hasRenderToCubemap = true;
+ hasTwoSidedStencil = true;
+ hasSRGBReadWrite = true;
+ hasNativeDepthTexture = true;
+ hasStencilInDepthTexture = true;
+ hasNativeShadowMap = (emul == kEmulPS3);
+
+ rendererString = "Emulated Shader Model 3.0 (XBOX360/PS3)";
+ vendorString = "Emulated";
+ fixedVersionString = "XBOX360/PS3 (emulated)";
+ break;
+ case kEmulDX11_9_1:
+ case kEmulDX11_9_3:
+ shaderCaps = emul == kEmulDX11_9_3 ? kShaderLevel3 : kShaderLevel2;
+ hasFixedFunction = emul == kEmulDX11_9_3;
+
+ maxLights = 8;
+ maxMRTs = 1;
+ hasAnisoFilter = true;
+ maxAnisoLevel = emul == kEmulDX11_9_3 ? 16 : 2;
+ hasMipLevelBias = true;
+
+ hasMultiSample = true;
+
+ hasComputeShader = false;
+ hasInstancing = false;
+
+ hasBlendSquare = true;
+ hasSeparateAlphaBlend = false;
+
+ hasS3TCCompression = true;
+ hasVertexTextures = true;
+
+ maxTexImageUnits = 16;
+ maxTexCoords = 8;
+
+ maxTexUnits = 8;
+
+ maxTextureSize = emul == kEmulDX11_9_3 ? 4096 : 2048;
+ maxCubeMapSize = emul == kEmulDX11_9_3 ? 4096 : 512;
+
+ supportsRenderTextureFormat[kRTFormatARGB32] = true;
+ supportsRenderTextureFormat[kRTFormatDepth] = true;
+ supportsRenderTextureFormat[kRTFormatShadowMap] = true;
+
+ has3DTexture = true;
+ npotRT = npot = kNPOTRestricted;
+
+ hasTiledGPU = true; // assume tiled GPU when emulating DX11 9.x
+ warnRenderTargetUnresolves = true;
+
+ hasRenderToTexture = true;
+ hasRenderToCubemap = false;
+ hasTwoSidedStencil = true;
+ hasSRGBReadWrite = true;
+ hasNativeDepthTexture = true;
+ hasStencilInDepthTexture = true;
+ hasNativeShadowMap = true;
+#if GFX_SUPPORTS_D3D11
+ d3d11.hasShadows10Level9 = true;
+#endif
+
+ rendererString = "Emulated DirectX 11 9.1 No Fixed Function Shaders";
+ vendorString = "Emulated";
+ fixedVersionString = "DirectX 11 9.1 (emulated)";
+ break;
+ }
+}
+
+void GraphicsCaps::InitializeOriginalEmulationCapsIfNeeded()
+{
+ if (!gDidInitializeOriginalCaps)
+ {
+ gDidInitializeOriginalCaps = true;
+ SET_ALLOC_OWNER(NULL);
+ gOriginalCaps = *this;
+ }
+}
+
+void GraphicsCaps::ResetOriginalEmulationCaps()
+{
+ gDidInitializeOriginalCaps = false;
+}
+
+bool GraphicsCaps::CheckEmulationSupported( Emulation emul )
+{
+ InitializeOriginalEmulationCapsIfNeeded();
+
+ GraphicsCaps emulatedCaps = gOriginalCaps;
+ emulatedCaps.ApplyEmulationSetingsOnly(gOriginalCaps, emul);
+
+ return CheckEmulationValid(gOriginalCaps, emulatedCaps);
+}
+
+bool EmulationWants16bitDepth( GraphicsCaps::Emulation emul )
+{
+ if( (emul == GraphicsCaps::kEmulGLES20)
+ && GetBuildTargetGroup(GetEditorUserBuildSettings().GetActiveBuildTarget()) == kPlatformAndroid
+ && !GetPlayerSettings().GetUse24BitDepthBuffer()
+ )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+GraphicsCaps::Emulation _CurrentEmulation = GraphicsCaps::kEmulNone;
+
+void GraphicsCaps::ApplyEmulationSettingsAffectingGUI()
+{
+ // well, not the best solution...
+
+ if( EmulationWants16bitDepth(_CurrentEmulation) )
+ GUIView::RecreateAllOnDepthBitsChange(32, 16);
+ else
+ GUIView::RecreateAllOnDepthBitsChange(16, 32);
+}
+
+GraphicsCaps::Emulation GraphicsCaps::GetEmulation() const
+{
+ return _CurrentEmulation;
+}
+
+void GraphicsCaps::SetEmulation( Emulation emul )
+{
+ if( emul == _CurrentEmulation )
+ return;
+
+ if (!CheckEmulationSupported(emul))
+ {
+ ErrorString("Attempting to switch to unsupported emulation");
+ return;
+ }
+
+ // must be released before the emulation is set, since the memory usage is estimated from the deviceCaps
+ RenderTexture::ReleaseAll();
+
+ _CurrentEmulation = emul;
+
+ InitializeOriginalEmulationCapsIfNeeded();
+
+ *this = gOriginalCaps;
+
+ GfxDevice& device = GetGfxDevice();
+ device.InvalidateState();
+ ShaderLab::SubProgram* nullShaders[kShaderTypeCount] = {0};
+ GraphicsHelper::SetShaders (device, nullShaders, 0);
+ InvalidateGraphicsStateInEditorWindows();
+
+ ApplyEmulationSettingsAffectingGUI();
+ ApplyEmulationSetingsOnly(gOriginalCaps, emul);
+ SharedCapsPostInitialize();
+
+ device.CommonReloadResources(GfxDevice::kReleaseRenderTextures | GfxDevice::kReloadShaders | GfxDevice::kReloadTextures);
+
+ #if GFX_SUPPORTS_D3D9
+ InitializeCombinerCapsD3D9();
+ #endif
+
+ // Give a chance for image effects (which mostly execute in edit mode
+ // as well) to recheck hardware support for them.
+ MonoBehaviour::RestartExecuteInEditModeScripts();
+
+ // update all lights
+ std::vector<Light*> lights;
+ Object::FindObjectsOfType (&lights);
+ for( std::vector<Light*>::iterator i = lights.begin(); i != lights.end(); ++i )
+ {
+ Light& light = (**i);
+ light.Precalc();
+ }
+}
+
+bool GraphicsCaps::IsEmulatingGLES20() const
+{
+ return _CurrentEmulation == kEmulGLES20;
+}
+
+#endif // UNITY_EDITOR
+
+
+std::string GraphicsCaps::CheckGPUSupported() const
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+ GfxDeviceRenderer type = GetGfxDevice().GetRenderer();
+
+ // OpenGL
+# if GFX_SUPPORTS_OPENGL
+ if (type == kGfxRendererOpenGL)
+ {
+ extern bool QueryExtensionGL (const char* ext);
+
+ // OS X 10.5 on GMA 950 has GL1.2. When we drop 10.5 support we can move
+ // up to GL1.4.
+ if (gl.glVersion < 12)
+ {
+ return Format("OpenGL 1.2 is required. Your GPU (%s) only supports OpenGL %i.%i", rendererString.c_str(), gl.glVersion/10, gl.glVersion%10);
+ }
+ // Require GLSL
+ if (!gl.hasGLSL)
+ {
+ return Format("OpenGL GLSL support is required. Your GPU (%s) does not support it", rendererString.c_str());
+ }
+ // Require ARB VP/FP
+ if (!QueryExtensionGL("GL_ARB_vertex_program") || !QueryExtensionGL("GL_ARB_fragment_program"))
+ {
+ return Format("OpenGL vertex/fragment program support is required. Your GPU (%s) does not support it", rendererString.c_str());
+ }
+
+ // Require VBO, multitexture, ...
+ if (gl.glVersion < 15 && !QueryExtensionGL("GL_ARB_vertex_buffer_object"))
+ {
+ return Format("OpenGL vertex buffer support is required. Your GPU (%s) does not support it", rendererString.c_str());
+ }
+ if (gl.glVersion < 13 && !QueryExtensionGL ("GL_ARB_multitexture"))
+ {
+ return Format("OpenGL multitexture support is required. Your GPU (%s) does not support it", rendererString.c_str());
+ }
+ if (gl.glVersion < 13 && !QueryExtensionGL ("GL_ARB_texture_cube_map"))
+ {
+ return Format("OpenGL cubemap support is required. Your GPU (%s) does not support it", rendererString.c_str());
+ }
+ if (gl.glVersion < 13 && !QueryExtensionGL ("GL_ARB_texture_env_dot3"))
+ {
+ return Format("OpenGL dot3 combiner support is required. Your GPU (%s) does not support it", rendererString.c_str());
+ }
+ }
+# endif // if GFX_SUPPORTS_OPENGL
+
+
+ // D3D9
+# if GFX_SUPPORTS_D3D9
+ if (type == kGfxRendererD3D9)
+ {
+ // Require SM2.0 at least.
+ // Note, vertex shaders can be reported as zero in case of software vertex processing.
+ // That is a valid & supported case (d3d runtime will run shaders on the CPU, but
+ // otherwise they will behave as supported).
+ const int kShaderVersion20 = (2 << 8) + 0;
+ const int d3dVS = LOWORD(d3d.d3dcaps.VertexShaderVersion);
+ const int d3dPS = LOWORD(d3d.d3dcaps.PixelShaderVersion);
+ if ((d3dVS != 0 && d3dVS < kShaderVersion20) || (d3dPS < kShaderVersion20))
+ {
+ return Format("DirectX9 GPU (Shader Model 2.0) is required.\r\nYour GPU (%s)\r\nonly supports Shader Model %i.%i", rendererString.c_str(), d3dPS>>8, d3dPS&0xFF);
+ }
+ }
+# endif // if GFX_SUPPORTS_D3D9
+
+ UNUSED(type);
+
+#endif
+ return ""; // all ok
+}
+
+
+void GraphicsCaps::SharedCapsPostInitialize()
+{
+ // do some sanity checks
+ Assert (!hasStencilInDepthTexture || (hasStencilInDepthTexture && hasNativeDepthTexture)); // stencil in depth texture implies native depth texture
+
+ // requirements for deferred lighting
+ const int systemMemoryMB = systeminfo::GetPhysicalMemoryMB();
+
+ hasPrePassRenderLoop =
+# if GFX_SUPPORTS_RENDERLOOP_PREPASS
+ (shaderCaps >= kShaderLevel3) && // needs SM3.0
+ hasRenderToTexture && // needs render textures
+ supportsRenderTextureFormat[kRTFormatDepth] && // needs depth RT
+ hasRenderTargetStencil && // needs stencil in RT
+ hasTwoSidedStencil && // needs two sided stencil
+ (systemMemoryMB==0 || systemMemoryMB >= 450) // disable on old devices that have <512MB RAM (check for zero in case platform doesn't implement it at all...)
+# else
+ false
+# endif
+ ;
+}
+
+
+void GraphicsCaps::InitWithBuildVersion()
+{
+ #if GFX_SUPPORTS_OPENGL
+ // Before 4.2, we never used native shadow maps on GL, so keep that behavior.
+ if (GetGfxDevice().GetRenderer() == kGfxRendererOpenGL)
+ {
+ // First restore original flag (same player instance can be
+ // reused across different content versions)
+ hasNativeShadowMap = gl.originalHasNativeShadowMaps;
+ supportsRenderTextureFormat[kRTFormatShadowMap] = hasNativeShadowMap;
+
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1))
+ {
+ hasNativeShadowMap = false;
+ supportsRenderTextureFormat[kRTFormatShadowMap] = false;
+ }
+ }
+ #endif
+}
+
+
+#if GFX_SUPPORTS_OPENGL || GFX_SUPPORTS_OPENGLES20
+bool FindGLExtension (const char* extensions, const char* ext)
+{
+ // Extension names should not have spaces, be NULL or empty
+ if (!ext || ext[0] == 0)
+ return false;
+ if (strchr(ext, ' '))
+ return false;
+
+ Assert (extensions != NULL);
+ if (!extensions)
+ return false;
+
+ const char* start = extensions;
+ for (;;)
+ {
+ const char* where = strstr (start, ext);
+ if (!where)
+ break;
+ const char* terminator = where + strlen (ext);
+ if (where == start || *(where - 1) == ' ')
+ {
+ if (*terminator == ' ' || *terminator == '\0')
+ return true;
+ }
+ start = terminator;
+ }
+ return false;
+}
+#endif // GFX_SUPPORTS_OPENGL || GFX_SUPPORTS_OPENGLES20
+
+#if GFX_SUPPORTS_OPENGLES20
+#if GFX_SUPPORTS_OPENGLES20
+# include "Runtime/GfxDevice/opengles20/IncludesGLES20.h"
+#endif
+
+bool QueryExtension (const char* ext)
+{
+ static const char* extensions = NULL;
+ if (!extensions)
+ extensions = (const char*)glGetString(GL_EXTENSIONS);
+ if (!extensions)
+ return false;
+ return FindGLExtension (extensions, ext);
+}
+
+#endif // GFX_SUPPORTS_OPENGLES20
+
+
+
+// -------------------------------------------------------------------
+
+
+
+#if ENABLE_UNIT_TESTS
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (GraphicsCapsTest)
+{
+ TEST (GraphicsCaps_DeviceIDs)
+ {
+ #if UNITY_EDITOR
+ if (gGraphicsCaps.GetEmulation() != GraphicsCaps::kEmulNone)
+ return;
+ #endif
+
+ const int vendorID = gGraphicsCaps.vendorID;
+ if (vendorID == 0)
+ return;
+
+ std::string vendor = ToLower(gGraphicsCaps.vendorString);
+
+ if (vendorID == 0x10de)
+ {
+ CHECK (vendor.find("nvidia") != std::string::npos);
+ }
+ if (vendorID == 0x1002)
+ {
+ CHECK (vendor.find("ati") != std::string::npos || vendor.find("amd") != std::string::npos);
+ }
+ if (vendorID == 0x8086)
+ {
+ CHECK (vendor.find("intel") != std::string::npos);
+ }
+ }
+}
+
+#endif // #if ENABLE_UNIT_TESTS
diff --git a/Runtime/Shaders/GraphicsCaps.h b/Runtime/Shaders/GraphicsCaps.h
new file mode 100644
index 0000000..0e1e37d
--- /dev/null
+++ b/Runtime/Shaders/GraphicsCaps.h
@@ -0,0 +1,435 @@
+#pragma once
+
+#include <string>
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+enum ShaderCapsLevel
+{
+ kShaderLevel2 = 20, // shader model 2.0 & 2.x
+ kShaderLevel3 = 30, // shader model 3.0
+ kShaderLevel4 = 40, // shader model 4.0
+ kShaderLevel4_1 = 41, // shader model 4.1
+ kShaderLevel5 = 50, // shader model 5.0
+};
+
+enum NPOTCaps
+{
+ kNPOTNone = 0, // no NPOT texture capability
+ kNPOTRestricted, // NPOT available with restrictions (no mips, no wrap mode, no compression)
+ kNPOTFull, // NPOT available, no restrictions
+};
+
+
+#if GFX_SUPPORTS_D3D9
+#include "PlatformDependent/Win/WinDriverUtils.h"
+#include "Runtime/GfxDevice/d3d/D3D9Includes.h"
+struct GraphicsCapsD3D9
+{
+ D3DCAPS9 d3dcaps;
+ bool hasBaseTextureFormat[kTexFormatPCCount];
+ bool hasTextureFormatA8;
+ bool hasTextureFormatL8;
+ bool hasTextureFormatA8L8;
+ bool hasTextureFormatL16;
+ bool hasATIDepthFormat16; // Has ATI DF16 render texture format?
+ bool hasNVDepthFormatINTZ;
+ bool hasNVDepthFormatRAWZ;
+ bool hasNULLFormat;
+ bool hasDepthResolveRESZ;
+
+ // ---- hardware/driver workarounds
+ bool slowINTZSampling; // very slow at using depth testing and sampling INTZ at the same time; prefer RESZ copy into separate surface
+};
+#endif
+
+#if GFX_SUPPORTS_D3D11
+enum DX11FeatureLevel {
+ kDX11Level9_1,
+ kDX11Level9_2,
+ kDX11Level9_3,
+ kDX11Level10_0,
+ kDX11Level10_1,
+ kDX11Level11_0,
+ kDX11LevelCount
+};
+
+struct GraphicsCapsD3D11
+{
+ UInt32 msaa; // regular backbuffer, bit for each sample count we want
+ UInt32 msaaSRGB; // sRGB backbuffer, bit for each sample count we want
+ DX11FeatureLevel featureLevel;
+ bool hasShadows10Level9;
+ bool buggyPartialPrecision10Level9; // half precision broken on 10level9 shaders (crashes)
+};
+#endif
+
+#if GFX_SUPPORTS_OPENGL
+struct GraphicsCapsGL
+{
+ int glVersion; // e.g. 20 for 2.0, 43 for 4.3
+ int vertexAttribCount; // How many generic vertex attributes are supported?
+
+ int nativeVPInstructions;
+ int nativeFPInstructions;
+ int nativeFPTexInstructions;
+ int nativeFPALUInstructions;
+ int nativeFPTemporaries;
+
+ int maxSamples;
+
+ bool hasGLSL; // GLSL
+
+ bool hasTextureEnvCombine3ATI; // GL_ATI_texture_env_combine3 - the preferred way
+ bool hasTextureEnvCombine3NV; // GL_NV_texture_env_combine4 - fallback to emulate with TNT combiners
+
+ #if UNITY_WIN
+ bool hasWglARBPixelFormat;
+ bool hasWglSwapControl;
+ #endif
+
+ bool hasArbMapBufferRange;
+ bool hasArbSync;
+
+ #if UNITY_OSX
+ bool hasAppleFence;
+ bool hasAppleFlushBufferRange;
+ #endif
+
+ bool hasFrameBufferBlit;
+
+ // ---- hardware/driver workarounds
+ bool buggyArbPrecisionHint; // ARB precision hint in FPs is broken, postprocess programs to remove it
+ bool cacheFPParamsWithEnvs; // For fragment programs, use Env parameters instead of Local ones, and cache their values
+ bool forceColorBufferWithDepthFBO; // For depth FBO, always use color buffer, with no color buffer it will be wrong
+ bool force24DepthForFBO; // 16 bit depth does not work for FBO depth textures
+ bool buggyPackedDepthStencil;
+ bool mustWriteToDepthBufferBeforeClear; // depth buffer will contain garbage if it isn't written to at least once per frame.
+ bool originalHasNativeShadowMaps;
+};
+#endif
+
+
+
+#if GFX_SUPPORTS_OPENGLES20
+struct GraphicsCapsGLES20
+{
+ int maxAttributes;
+ int maxVaryings;
+ int maxSamples;
+ bool hasGLSL;
+ bool hasVBOOrphaning;
+ bool slowAlphaTest;
+ bool slowDynamicVBO; // will force dynamic draw from mem (except static batch indices)
+ bool forceStaticBatchFromMem; // will force static batch indices to be used from mem
+
+ // features
+
+ bool hasBinaryShaders;
+ bool has24DepthForFBO;
+ bool hasMapbuffer;
+ bool hasMapbufferRange;
+ bool hasDebugMarkers;
+ bool hasDiscardFramebuffer;
+ bool hasHalfLinearFilter;
+
+ bool hasAppleMSAA;
+ bool hasImgMSAA;
+
+ // bugs workaround
+
+ bool buggyColorMaskBlendMSAA; // on some ios msaa+color mask+blend results in crazy rendering artefacts
+ bool buggyDisableVAttrKeepsActive; // on some mali drivers vattrs are kept active even after disabling them
+ bool forceHighpFSPrec; // on some nvidia drivers there is a bug: they fail to report samplers if default prec is not high
+ bool buggyVFetchPatching; // some adreno drivers fails to patch vfetch instructions (if vertex layout changed)
+ bool buggyVprogTextures; // some mali drivers have crashes in shader compiler if vprog texture is used
+ bool needToRenderWatermarkDueToDrawFromMemoryBuggy; // on some pvr it may crash when rendering from mem (it seems one shader used is culprit)
+ bool needFlushAfterTextureUpload;
+
+ // vendor specific
+
+ bool hasNLZ; // nvidia non-linear z
+ bool hasNVMRT; // nvidia mrt support
+ bool hasAlphaTestQCOM; // adreno special gles11 alpha test
+};
+#endif
+
+#if GFX_SUPPORTS_OPENGLES30
+struct GraphicsCapsGLES30
+{
+ GraphicsCapsGLES30 (void)
+ : maxAttributes (16)
+ , maxVaryings (16)
+ , maxSamples (4)
+ , useProgramBinary (true)
+ , useMapBuffer (true)
+ , useTFSkinning (true)
+ , hasDebugMarkers (false)
+ , hasAlphaTestQCOM (false)
+ {
+ }
+
+ // Limits.
+ int maxAttributes;
+ int maxVaryings;
+ int maxSamples; // Max sample count for renderbuffers.
+
+ // Feature configuration.
+ bool useProgramBinary; // ProgramBinary support is in core, but in case of we want to ignore it on some platforms.
+ bool useMapBuffer; // One some platforms mapping buffers is very slow.
+ bool useTFSkinning; // Use transform feedback for skinning.
+
+ // Extension bits.
+ bool hasDebugMarkers;
+ bool hasAlphaTestQCOM;
+};
+#endif
+
+#if GFX_SUPPORTS_OPENGL || GFX_SUPPORTS_OPENGLES20
+bool FindGLExtension (const char* extensions, const char* ext);
+#endif
+
+#if GFX_SUPPORTS_OPENGLES20
+bool QueryExtension (const char* ext);
+#endif
+
+
+struct GraphicsCaps
+{
+ GraphicsCaps()
+ {
+ ::memset(this, 0x00, sizeof(GraphicsCaps));
+
+ for (int i = 0 ; i < kTexFormatPCCount; ++i)
+ supportsTextureFormat[i] = true;
+
+ // Default RT format is always enabled until we don't have render-to-texture at all
+ // It is dummy value used to select most appropriate rt format
+ supportsRenderTextureFormat[kRTFormatDefault] = true;
+ supportsRenderTextureFormat[kRTFormatDefaultHDR] = true;
+
+ new (&rendererString) StaticString();
+ new (&vendorString) StaticString();
+ new (&driverVersionString) StaticString();
+ new (&fixedVersionString) StaticString();
+ new (&driverLibraryString) StaticString();
+
+ shaderCaps = kShaderLevel2;
+ videoMemoryMB = 16.0f;
+
+ maxLights = 8;
+ maxAnisoLevel = 1;
+ maxTexImageUnits = 8;
+ maxTexCoords = 1;
+ maxTexUnits = 1;
+
+ maxTextureSize = 256;
+ maxCubeMapSize = 64;
+ maxRenderTextureSize = 128;
+ maxMRTs = 1;
+
+ hasFixedFunction = true;
+ npotRT = npot = kNPOTNone;
+ hasNonFullscreenClear = true;
+ hasShadowCollectorPass = true;
+ hasHighPrecisionTextureCombiners = true;
+
+ hasGrabPass = true;
+ }
+
+ void SharedCapsPostInitialize();
+
+ // ---------- caps common for all devices
+ StaticString rendererString; // graphics card name
+ StaticString vendorString; // graphics card vendor name
+ StaticString driverVersionString; // (GL) version as reported by the driver
+ StaticString fixedVersionString; // (GL) correct GL version appended in front by us
+ StaticString driverLibraryString; // Name of driver's DLL and version
+ int vendorID;
+ int rendererID;
+
+ ShaderCapsLevel shaderCaps; // Generic "shader capability level"
+
+ float videoMemoryMB; // Approx. amount of video memory in MB. Used to limit texture, render target and so on sizes to sane values.
+
+ int maxVSyncInterval; // Max frames that device can wait for vertical blank
+ int maxLights; // vertex light count
+ int maxAnisoLevel;
+ int maxTexImageUnits; // Part of arb-fragment program (otherwise they match textureUnitCount)
+ int maxTexCoords;
+ int maxTexUnits;
+
+ int maxTextureSize;
+ int maxCubeMapSize;
+ int maxRenderTextureSize; // usually maxTextureSize, except on some crappy cards
+
+ int maxMRTs;
+
+ bool hasAnisoFilter; // has anisotropic filtering?
+ bool hasMipLevelBias; // can apply bias for mips?
+ bool hasMipMaxLevel; // can specify max mip level
+
+ bool hasFixedFunction; // Supports T&L and texture combine stages
+
+ bool hasMultiSample;
+ bool hasMultiSampleAutoResolve; // uses texture instead, and under-the-hood rb will be auto-resolved to it
+
+ bool hasBlendSquare;
+ bool hasSeparateAlphaBlend;
+ bool hasBlendSub; // both sub and revsub
+ bool hasBlendMinMax; // max,min
+ bool hasBlendLogicOps; // kBlendOpLogical*
+
+ bool hasS3TCCompression;
+ bool hasVertexTextures; // Can read textures in a vertex shader?
+
+ bool hasTimerQuery;
+
+ bool hasAutoMipMapGeneration; // can auto-generate mipmaps for textures?
+
+ bool supportsTextureFormat[kTexFormatTotalCount];
+ bool supportsRenderTextureFormat[kRTFormatCount];
+
+ bool has3DTexture;
+
+ NPOTCaps npot;
+ NPOTCaps npotRT;
+
+ bool hasSRGBReadWrite;
+ bool hasComputeShader;
+ bool hasInstancing;
+ bool hasNonFullscreenClear; // Can do clears on non-full screen? (e.g. D3D11 can not)
+
+ bool hasRenderToTexture; // We have render-to-texture functionality
+ bool hasRenderToCubemap; // We have render-to-cubemap functionality
+ bool hasRenderTo3D; // We have render-to-volume functionality
+ bool hasStencil; // Stencil support good enough to be used by the users via the shaderlab state.
+ bool hasRenderTargetStencil; // Has sane way of having stencil buffer on render targets
+ bool hasNativeDepthTexture; // Depth textures come from actual depth buffer
+ bool hasStencilInDepthTexture; // Has native depth texture AND stencil buffer of it can be used at the same time
+ bool hasNativeShadowMap; // Depth textures have native shadow map comparison sampling
+ bool hasShadowCollectorPass; // mobiles don't usage collector pass, go direct SMs!
+ bool hasTiledGPU; // Uses tiled rendering (clear/discard preferred!)
+
+ bool hasTwoSidedStencil;
+ bool hasHighPrecisionTextureCombiners;
+
+ bool has16BitFloatVertex; // Supports at least 2D and 4D vertex channels as 16 bit floats
+ bool needsToSwizzleVertexColors; // Should vertex colors be passed as B,G,R,A bytes instead of R,G,B,A
+
+ bool disableSubTextureUpload; // Can we do UploadTextureSubData2D?
+ bool warnRenderTargetUnresolves; // Warn when doing RT un-resolves
+
+ bool hasGrabPass; // Can read from active render target back into a render texture?
+
+ // cross platform caps initialized in SharedCapsPostInitialize
+ bool hasPrePassRenderLoop;
+
+ bool SupportsRGBM() const { return maxTexUnits >= 3 && hasHighPrecisionTextureCombiners; }
+
+ std::string CheckGPUSupported() const;
+
+ // ---- hardware/driver workarounds for all renderers
+
+ bool disableSoftShadows; // Soft shadows should work, but the driver is buggy on those shaders
+ bool buggyCameraRenderToCubemap;// Render to cubemap sort-of-does-not-work (but works for point light shadows).
+ bool buggyFullscreenFSAA; // FSAA in fullscreen is buggy.
+ bool buggyTextureBothColorAndDepth; // Can't have render target where both color & depth will be sampled as textures
+ bool buggyDynamicVBOWithTangents; // DynamicVBO with tangents is buggy
+ bool buggyMipmappedCubemaps; // Cubemaps with mipmaps are buggy; keep only 1st mip level
+ bool buggyMipmapped3DTextures; // 3D textures with mipmaps are buggy; keep only 1st mip level
+ bool buggyShadowMapBilinearSampling; // Never use hardware bilinear PCF on shadow maps due to bugs?
+ bool buggySpotNativeShadowMap; // Never use hardware shadow maps on spot lights
+ bool buggyTimerQuery; // Has timer queries, but they aren't very good!
+
+ // ---- caps specific for renderers
+
+#if GFX_SUPPORTS_OPENGL
+ GraphicsCapsGL gl;
+#endif
+#if GFX_SUPPORTS_OPENGLES20
+ GraphicsCapsGLES20 gles20;
+#endif
+#if GFX_SUPPORTS_D3D9
+ GraphicsCapsD3D9 d3d;
+#endif
+#if GFX_SUPPORTS_D3D11
+ GraphicsCapsD3D11 d3d11;
+#endif
+#if GFX_SUPPORTS_OPENGLES30
+ GraphicsCapsGLES30 gles30;
+#endif
+
+ #if GFX_SUPPORTS_OPENGL
+ void InitGL();
+ #endif
+ #if GFX_SUPPORTS_D3D9
+ void InitD3D9();
+ #endif
+ #if GFX_SUPPORTS_D3D11
+ void InitD3D11();
+ #endif
+ #if GFX_SUPPORTS_NULL
+ void InitNull();
+ #endif
+ #if GFX_SUPPORTS_MOLEHILL
+ void InitMolehill();
+ #endif
+ #if GFX_SUPPORTS_XENON
+ void InitXenon();
+ #endif
+ #if GFX_SUPPORTS_GCM
+ void InitGcm();
+ UInt32 videoMemoryStart;
+ #endif
+ #if GFX_SUPPORTS_HOLLYWOOD
+ void InitHollywood();
+ #endif
+ #if GFX_SUPPORTS_OPENGLES20
+ void InitGLES20();
+ #endif
+ #if GFX_SUPPORTS_OPENGLES30
+ void InitGLES30();
+ #endif
+
+ void InitWithBuildVersion();
+
+
+ // Implemented in GraphicsCaps.cpp
+ #if UNITY_EDITOR
+ enum Emulation {
+ kEmulNone, // whatever card naturally has
+ kEmulSM3, // Shader Model 3.0
+ kEmulSM2, // Shader Model 2.0, but with 4 texture stages
+ kEmulGLES20, // OpenGL ES 2.0
+ kEmulXBox360,
+ kEmulPS3,
+ kEmulDX11_9_1, // DirectX 11 Feature Level 9.1, No Fixed Function shaders (Shader Model 2)
+ kEmulDX11_9_3, // DirectX 11 Feature Level 9.1, Fixed Function shaders (Shader Model 3)
+ kEmulCount
+ };
+ Emulation GetEmulation() const;
+ void SetEmulation( Emulation emul );
+ bool IsEmulatingGLES20() const;
+ bool CheckEmulationSupported( Emulation emul );
+ void InitializeOriginalEmulationCapsIfNeeded();
+ void ResetOriginalEmulationCaps();
+
+ static void ApplyEmulationSettingsAffectingGUI();
+
+ private:
+ void ApplyEmulationSetingsOnly( const GraphicsCaps& origCaps, const Emulation emul );
+ #endif
+
+
+private:
+
+ #if GFX_SUPPORTS_OPENGL
+ void AdjustBuggyVersionGL( int& version, int& major, int& minor ) const;
+ void DetectDriverBugsGL( int version );
+ #endif
+ #if GFX_SUPPORTS_D3D9
+ void DetectDriverBugsD3D9( UInt32 vendorCode, const windriverutils::VersionInfo& version );
+ #endif
+};
+
+extern GraphicsCaps gGraphicsCaps;
diff --git a/Runtime/Shaders/Material.cpp b/Runtime/Shaders/Material.cpp
new file mode 100644
index 0000000..3e6aaf5
--- /dev/null
+++ b/Runtime/Shaders/Material.cpp
@@ -0,0 +1,1096 @@
+#include "UnityPrefix.h"
+#include "Material.h"
+#include "Shader.h"
+#include "ShaderNameRegistry.h"
+#include "External/shaderlab/Library/properties.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Graphics/ProceduralMaterial.h"
+#include "Runtime/GfxDevice/GfxDisplayList.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Profiler/Profiler.h"
+
+#if UNITY_EDITOR
+#include "External/shaderlab/Library/SLParserData.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/Backend/ScriptingInvocation.h"
+
+extern ShaderLab::ShaderState* g_EditorPixelShaderOverride;
+#endif
+
+
+PROFILER_INFORMATION(gSetShadowCasterPass, "Shader.SetShadowCasterPass", kProfilerRender);
+PROFILER_INFORMATION(gSetShadowCollectorPass, "Shader.SetShadowCollectorPass", kProfilerRender);
+
+
+using namespace std;
+
+namespace Unity
+{
+
+template<class TransferFunc>
+void Material::Transfer (TransferFunc& transfer)
+{
+ transfer.SetVersion (3);
+ Super::Transfer (transfer);
+ transfer.Transfer (m_Shader, "m_Shader");
+ transfer.Transfer (m_ShaderKeywords, "m_ShaderKeywords");
+ transfer.Transfer (m_CustomRenderQueue, "m_CustomRenderQueue");
+
+#if UNITY_EDITOR
+ if (transfer.IsBuildingTargetPlatform(kBuild_Android) && m_Shader && m_Shader->HasClip())
+ {
+ DebugStringToFile ( "Shader is using clip instruction (usually caused by alpha test). It might cause problems on some Qualcomm/Adreno drivers.",
+ 0, __FILE__, __LINE__, kAssetImportWarning, GetInstanceID()
+ );
+
+ }
+#endif
+
+ // Cull unused properties when making build!
+ #if UNITY_EDITOR
+ if ((transfer.GetFlags () & kBuildPlayerOnlySerializeBuildProperties))
+ {
+ Shader *shader = m_Shader;
+ if (shader)
+ {
+ UnityPropertySheet tempProps = m_SavedProperties;
+ tempProps.CullUnusedProperties (shader->GetParsedForm());
+ transfer.Transfer (tempProps, "m_SavedProperties");
+ }
+ else
+ {
+ UnityPropertySheet tempProps;
+ transfer.Transfer (tempProps, "m_SavedProperties");
+ }
+ }
+ else
+ #endif
+ {
+ TRANSFER (m_SavedProperties);
+ }
+}
+
+
+void Material::Reset()
+{
+ Super::Reset();
+
+ m_CustomRenderQueue = -1;
+
+ ClearProperties();
+ m_SavedProperties = UnityPropertySheet();
+
+ Shader *shader = m_Shader;
+ if( !shader )
+ shader = Shader::GetDefault();
+
+ BuildShaderKeywordSet (); // build keywords before properties, so that pass hashing gets correct keywords
+ BuildProperties ();
+#if UNITY_EDITOR
+ ResetDefaultTextures (true);
+ if (GetCachedScriptingObject())
+ ApplyMaterialPropertyDrawers();
+#endif
+}
+
+
+int Material::GetActualRenderQueue() const
+{
+ // Use custom render queue if set (case 548478)
+ if (m_CustomRenderQueue >= 0)
+ return m_CustomRenderQueue;
+
+ const Shader* shader = GetShader();
+ Assert (shader);
+ return shader->GetShaderLabShader()->GetRenderQueue();
+}
+
+
+void Material::BuildProperties ()
+{
+ SET_ALLOC_OWNER(NULL);
+ SAFE_RELEASE_LABEL(m_Properties,kMemShader);
+ Shader *shader = m_Shader;
+ if( !shader )
+ shader = Shader::GetDefault();
+
+ // This happens on Flash when loading some materials - at AwakeFromLoad
+ // time, the shader is not parsed yet (?). Just return in that case, the
+ // properties will be build later on demand. Worst that can happen is that
+ // for one frame shadow casters might get batched slightly wrong.
+ if (!shader->GetShaderLabShader())
+ return;
+
+ // Build the shaderlab property sheet.
+ m_Properties = shader->MakeProperties();
+ m_Properties->SetOwnerMaterial(this);
+
+ // Make sure our serialized properties (m_SavedProperties) have
+ // all the properties from the shader's properties block.
+ // (we might not be up-to-date if the shader has changed and added more
+ // properties).
+ m_SavedProperties.AddNewShaderlabProps (*shader->GetShaderLabShader()->GetDefaultProperties());
+
+ /// Get the properties from the material.
+ m_SavedProperties.AssignDefinedPropertiesTo (*m_Properties);
+
+ if( m_Shader )
+ m_Shader->AddMaterialUser( m_ShaderUserNode );
+
+ UpdateHashes();
+}
+
+
+void Material::UpdateHashesOnPropertyChange (ShaderLab::FastPropertyName name)
+{
+ Shader* shader = m_Shader;
+ if (!shader || !m_Properties)
+ return;
+ ShaderLab::Pass* passCaster = shader->GetShadowCasterPass();
+ if (passCaster)
+ {
+ if (passCaster->IsPropertyAffectingPass(name))
+ m_ShadowCasterHash = passCaster->ComputePassValuesHash (m_ShaderKeywordSet, m_Properties);
+ }
+ ShaderLab::Pass* passCollector = shader->GetShadowCollectorPass();
+ if (passCollector)
+ {
+ if (passCollector->IsPropertyAffectingPass(name))
+ m_ShadowCollectorHash = passCollector->ComputePassValuesHash (m_ShaderKeywordSet, m_Properties);
+ }
+
+ const dynamic_array<int>& propsAffectingBlocks = shader->GetShaderLabShader()->GetPropsAffectingStateBlocks();
+ if (std::find(propsAffectingBlocks.begin(), propsAffectingBlocks.end(), name.index) != propsAffectingBlocks.end())
+ {
+ m_StateKeyHash = ShaderLab::ComputeStateBlockValuesHash (propsAffectingBlocks, m_Properties);
+ shader->GetShaderLabShader()->CreateStateBlocksForKey (m_StateKeyHash, m_Properties);
+ }
+}
+
+void Material::UpdateHashes ()
+{
+ m_ShadowCasterHash = 0;
+ m_ShadowCollectorHash = 0;
+ m_StateKeyHash = 0;
+ Shader* shader = m_Shader;
+ if (!shader || !m_Properties)
+ return;
+ ShaderLab::Pass* passCaster = shader->GetShadowCasterPass();
+ if (passCaster)
+ m_ShadowCasterHash = passCaster->ComputePassValuesHash (m_ShaderKeywordSet, m_Properties);
+ ShaderLab::Pass* passCollector = shader->GetShadowCollectorPass();
+ if (passCollector)
+ m_ShadowCollectorHash = passCollector->ComputePassValuesHash (m_ShaderKeywordSet, m_Properties);
+
+ m_StateKeyHash = ShaderLab::ComputeStateBlockValuesHash (shader->GetShaderLabShader()->GetPropsAffectingStateBlocks(), m_Properties);
+ shader->GetShaderLabShader()->CreateStateBlocksForKey (m_StateKeyHash, m_Properties);
+}
+
+
+void Material::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ BuildShaderKeywordSet (); // build keywords before properties, so that pass hashing gets correct keywords
+
+ ClearProperties ();
+
+ // Enforce that everything is preloaded in the player, reduces hiccups at runtime
+ // Don't do that in the editor because it can easily run out of memory and increase build times.
+ #if !UNITY_EDITOR
+ BuildProperties();
+ #endif
+}
+
+
+Material::Material(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_ShaderUserNode(this)
+{
+ m_Properties = NULL;
+ m_PropertiesDirty = false;
+ m_Shader = 0;
+ m_CustomRenderQueue = -1;
+ m_ShadowCasterHash = 0;
+ m_ShadowCollectorHash = 0;
+ m_StateKeyHash = 0;
+}
+
+Material::~Material()
+{
+ SAFE_RELEASE_LABEL(m_Properties, kMemShader);
+ InvalidateDisplayLists();
+}
+
+void Material::SetShader (Shader *s)
+{
+ m_ShaderUserNode.RemoveFromList(); // remove ourselves from old shader's users
+
+ m_Shader = s;
+ if( !s )
+ {
+ SetDirty ();
+ return;
+ }
+
+ BuildProperties ();
+
+ SetDirty ();
+ InvalidateDisplayLists();
+}
+
+Material *Material::CreateMaterial (const char *shaderStr, int hideFlags, bool scriptingObjectIsBeingCreated)
+{
+ // Create temporary shader
+ Shader *shader = NEW_OBJECT (Shader);
+ shader->Reset();
+
+ shader->SetHideFlags(hideFlags);
+ shader->SetScript (shaderStr);
+ #if UNITY_EDITOR
+ shader->GetErrors().LogErrors (shader->GetName(), shader->GetNamedObjectName(), shader, shader->GetInstanceID());
+ #endif
+
+ shader->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+
+ // TODO: in editor parse shaderlab errors to console
+ //shader->ParseShaderErrorsToConsole();
+ return CreateMaterial(*shader, hideFlags, scriptingObjectIsBeingCreated);
+}
+
+Material *Material::CreateMaterial (Shader& shader, int hideFlags, bool scriptingObjectIsBeingCreated)
+{
+ // Create temporary material
+ Material *mat = CreateObjectFromCode<Material>();
+
+ mat->SetHideFlags(hideFlags);
+ mat->SetName(shader.GetName ());
+ mat->m_Shader = &shader;
+
+ mat->BuildProperties ();
+
+#if UNITY_EDITOR
+ mat->ResetDefaultTextures (true);
+ if (!scriptingObjectIsBeingCreated)
+ mat->ApplyMaterialPropertyDrawers();
+#endif
+
+ return mat;
+}
+
+Material *Material::CreateMaterial (const Material& material, int hideFlags, bool scriptingObjectIsBeingCreated)
+{
+ // Create temporary material
+ Material *mat = CreateObjectFromCode<Material>();
+
+ mat->SetHideFlags(hideFlags);
+ mat->SetName(material.GetName ());
+
+ mat->m_Shader = material.m_Shader;
+ mat->m_SavedProperties.AddNewSerializedProps (material.m_SavedProperties);
+ mat->BuildProperties ();
+
+ return mat;
+}
+
+Material *Material::GetDefault ()
+{
+ static Material* s_DefaultMaterial = NULL;
+ if (!s_DefaultMaterial) {
+ s_DefaultMaterial = CreateObjectFromCode<Material>();
+ s_DefaultMaterial->SetHideFlags (kHideAndDontSave);
+ }
+ return s_DefaultMaterial;
+}
+
+Material *Material::GetDefaultDiffuseMaterial ()
+{
+ #if WEBPLUG
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion_OldWebResourcesAdded))
+ return GetBuiltinOldWebResource<Material> ("Default-Diffuse.mat");
+ #endif
+
+ #if UNITY_EDITOR
+ // In the editor, important to return an asset here: e.g. if we create a cube,
+ // then we want to turn that into a prefab with a proper material.
+ return GetBuiltinExtraResource<Material> ("Default-Diffuse.mat");
+ #endif
+
+ static PPtr<Material> s_DefaultDiffuseMaterial;
+ if (s_DefaultDiffuseMaterial.IsNull())
+ {
+ Shader* shader = GetScriptMapper().FindShader("Diffuse");
+ if (!shader)
+ shader = Shader::GetDefault();
+ s_DefaultDiffuseMaterial = CreateMaterial(*shader, kHideAndDontSave);
+ }
+ return s_DefaultDiffuseMaterial;
+}
+
+struct ApplyKeywords
+{
+ ShaderKeywordSet prev;
+ ApplyKeywords (ShaderKeywordSet mask) {
+ prev = g_ShaderKeywords;
+ g_ShaderKeywords.SetMask (prev.GetMask () | mask.GetMask ());
+ }
+ ~ApplyKeywords () {
+ g_ShaderKeywords = prev;
+ }
+};
+
+const ChannelAssigns* Material::SetPass (int passNo, int subshaderIndex, bool allowRecording)
+{
+ ApplyKeywords applyKeywords (m_ShaderKeywordSet);
+
+ Shader *shader = m_Shader;
+ if (!shader)
+ shader = Shader::GetDefault();
+ EnsurePropertiesExist();
+
+ if (m_PropertiesDirty)
+ InvalidateDisplayLists();
+
+#if GFX_SUPPORTS_DISPLAY_LISTS
+
+ // editor shader override won't work with recorded display lists
+ #if UNITY_EDITOR
+ if (g_EditorPixelShaderOverride)
+ allowRecording = false;
+ #endif
+
+ CachedShaderPass* cachedPass = NULL;
+ if (allowRecording && !m_PropertiesDirty)
+ {
+ if (subshaderIndex >= m_CachedSubShaders.size())
+ m_CachedSubShaders.resize(subshaderIndex + 1);
+ CachedSubShader& subshader = m_CachedSubShaders[subshaderIndex];
+ if (passNo >= subshader.passes.size())
+ subshader.passes.resize_initialized(passNo + 1);
+ cachedPass = &subshader.passes[passNo];
+ }
+
+ GfxDevice& device = GetGfxDevice();
+
+ UInt64 shaderKeywords = g_ShaderKeywords.GetMask();
+ using ShaderLab::g_GlobalFogMode;
+ bool recording = false;
+ if (cachedPass)
+ {
+ if (cachedPass->displayList &&
+ cachedPass->shaderKeywords == shaderKeywords &&
+ cachedPass->globalFogMode == g_GlobalFogMode)
+ {
+ cachedPass->displayList->Call();
+ return cachedPass->channelAssigns;
+ }
+ SAFE_RELEASE(cachedPass->displayList);
+ if (shader->CanPassBeRecorded(subshaderIndex, passNo))
+ recording = device.BeginRecording();
+ }
+
+#endif
+
+ const ChannelAssigns* channels;
+ channels = shader->SetPass(subshaderIndex, passNo, m_StateKeyHash, &GetProperties());
+
+#if GFX_SUPPORTS_DISPLAY_LISTS
+ if (recording && device.EndRecording(&cachedPass->displayList))
+ {
+ cachedPass->channelAssigns = channels;
+ cachedPass->shaderKeywords = shaderKeywords;
+ cachedPass->globalFogMode = g_GlobalFogMode;
+ }
+#endif
+ return channels;
+}
+
+const ChannelAssigns* Material::SetPassWithShader( int passNo, Shader* shader, int subshaderIndex )
+{
+ if (shader == m_Shader)
+ return SetPass(passNo, subshaderIndex);
+
+ ApplyKeywords applyKeywords (m_ShaderKeywordSet);
+ Assert(shader != NULL);
+ return shader->SetPass( subshaderIndex, passNo, m_StateKeyHash, &GetProperties() );
+}
+
+const ChannelAssigns* Material::SetShadowCasterPassWithShader(Shader* shader, int subshaderIndex)
+{
+ if (shader == m_Shader)
+ return SetShadowCasterPass(subshaderIndex);
+
+ ApplyKeywords applyKeywords (m_ShaderKeywordSet);
+ PROFILER_AUTO(gSetShadowCasterPass, this)
+ ShaderLab::Pass* pass = shader->GetShadowCasterPassToUse(subshaderIndex);
+ return pass->ApplyPass(m_StateKeyHash, &GetProperties());
+}
+
+const ChannelAssigns* Material::SetShadowCasterPass(int subshaderIndex)
+{
+ PROFILER_AUTO(gSetShadowCasterPass, this)
+
+ ApplyKeywords applyKeywords (m_ShaderKeywordSet);
+ Shader *shader = GetShader();
+ ShaderLab::Pass* pass = shader->GetShadowCasterPassToUse(subshaderIndex);
+ EnsurePropertiesExist();
+
+#if GFX_SUPPORTS_DISPLAY_LISTS
+ if (m_PropertiesDirty)
+ InvalidateDisplayLists();
+
+ UInt64 shaderKeywords = g_ShaderKeywords.GetMask();
+ bool recording = false;
+ bool allowRecording = true;
+
+ // editor shader override won't work with recorded display lists
+ #if UNITY_EDITOR
+ if (g_EditorPixelShaderOverride)
+ allowRecording = false;
+ #endif
+
+ GfxDevice& device = GetGfxDevice();
+ CachedShaderPass* cachedPass = NULL;
+ if (!m_PropertiesDirty && allowRecording)
+ {
+ if (subshaderIndex >= m_CachedSubShaders.size())
+ m_CachedSubShaders.resize(subshaderIndex + 1);
+
+ CachedSubShader& subshader = m_CachedSubShaders[subshaderIndex];
+ cachedPass = &subshader.shadowCasterPass;
+ Assert (cachedPass != NULL);
+
+ if (cachedPass->displayList &&
+ cachedPass->shaderKeywords == shaderKeywords)
+ {
+ cachedPass->displayList->Call();
+ return cachedPass->channelAssigns;
+ }
+ SAFE_RELEASE(cachedPass->displayList);
+ if (pass->CanPassBeRecorded())
+ recording = device.BeginRecording();
+ }
+#endif
+
+ const ChannelAssigns* channels;
+ channels = pass->ApplyPass(m_StateKeyHash, &GetProperties());
+
+#if GFX_SUPPORTS_DISPLAY_LISTS
+ if (recording && device.EndRecording(&cachedPass->displayList))
+ {
+ cachedPass->channelAssigns = channels;
+ cachedPass->shaderKeywords = shaderKeywords;
+ }
+#endif
+
+ return channels;
+}
+
+const ChannelAssigns* Material::SetShadowCollectorPassWithShader(Shader* shader, int subshaderIndex)
+{
+ if (shader == m_Shader)
+ return SetShadowCollectorPass(subshaderIndex);
+
+ ApplyKeywords applyKeywords (m_ShaderKeywordSet);
+ PROFILER_AUTO(gSetShadowCollectorPass, this)
+ ShaderLab::Pass* pass = shader->GetShadowCollectorPassToUse(subshaderIndex);
+ return pass->ApplyPass(m_StateKeyHash, &GetProperties());
+}
+
+const ChannelAssigns* Material::SetShadowCollectorPass(int subshaderIndex)
+{
+ PROFILER_AUTO(gSetShadowCollectorPass, this)
+ ApplyKeywords applyKeywords (m_ShaderKeywordSet);
+ Shader *shader = GetShader();
+ ShaderLab::Pass* pass = shader->GetShadowCollectorPassToUse(subshaderIndex);
+ EnsurePropertiesExist();
+
+#if GFX_SUPPORTS_DISPLAY_LISTS
+ if (m_PropertiesDirty)
+ InvalidateDisplayLists();
+
+ UInt64 shaderKeywords = g_ShaderKeywords.GetMask();
+ bool recording = false;
+ bool allowRecording = true;
+
+ // editor shader override won't work with recorded display lists
+#if UNITY_EDITOR
+ if (g_EditorPixelShaderOverride)
+ allowRecording = false;
+#endif
+
+ GfxDevice& device = GetGfxDevice();
+ CachedShaderPass* cachedPass = NULL;
+ if (!m_PropertiesDirty && allowRecording)
+ {
+ if (subshaderIndex >= m_CachedSubShaders.size())
+ m_CachedSubShaders.resize(subshaderIndex + 1);
+
+ CachedSubShader& subshader = m_CachedSubShaders[subshaderIndex];
+ cachedPass = &subshader.shadowCollectorPass;
+ Assert (cachedPass != NULL);
+
+ if (cachedPass->displayList &&
+ cachedPass->shaderKeywords == shaderKeywords)
+ {
+ cachedPass->displayList->Call();
+ return cachedPass->channelAssigns;
+ }
+ SAFE_RELEASE(cachedPass->displayList);
+ if (pass->CanPassBeRecorded())
+ recording = device.BeginRecording();
+ }
+#endif
+
+ const ChannelAssigns* channels;
+ channels = pass->ApplyPass(m_StateKeyHash, &GetProperties());
+
+#if GFX_SUPPORTS_DISPLAY_LISTS
+ if (recording && device.EndRecording(&cachedPass->displayList))
+ {
+ cachedPass->channelAssigns = channels;
+ cachedPass->shaderKeywords = shaderKeywords;
+ }
+#endif
+ return channels;
+}
+
+const Shader *Material::GetShader() const
+{
+ const Shader *shader = m_Shader;
+ if (shader)
+ return shader;
+ else
+ return Shader::GetDefault();
+}
+
+PPtr<Shader> Material::GetShaderPPtr() const
+{
+ return m_Shader;
+}
+
+
+Shader *Material::GetShader()
+{
+ Shader *shader = m_Shader;
+ if (shader)
+ return shader;
+ else
+ return Shader::GetDefault();
+}
+std::string Material::GetTag( const string& tag, bool currentSubShaderOnly, const string& defaultValue ) const
+{
+ Shader* shader = m_Shader;
+ if (!shader)
+ return defaultValue;
+ int tagValueID = shader->GetShaderLabShader()->GetTag (ShaderLab::GetShaderTagID(tag), currentSubShaderOnly);
+ if (tagValueID < 0)
+ return defaultValue;
+ return ShaderLab::GetShaderTagName(tagValueID);
+}
+
+int Material::GetPassCount()
+{
+ Shader* shader = m_Shader;
+ if( !shader )
+ shader = Shader::GetDefault();
+ return shader->GetShaderLabShader()->GetActiveSubShader().GetValidPassCount();
+}
+
+Material& Material::GetInstantiatedMaterial (Material* material, Object& renderer, bool allowInEditMode)
+{
+ if (material == NULL)
+ material = GetDefaultDiffuseMaterial();
+
+ if (material->m_Owner == PPtr<Object> (&renderer))
+ return *material;
+ else
+ {
+ if (!allowInEditMode && !IsWorldPlaying())
+ ErrorStringObject("Instantiating material due to calling renderer.material during edit mode. This will leak materials into the scene. You most likely want to use renderer.sharedMaterial instead.", &renderer);
+
+ Material* instance;
+ if (material->GetClassID()==ProceduralMaterial::GetClassIDStatic())
+ {
+ instance = static_cast<ProceduralMaterial*>(material)->Clone();
+ }
+ else
+ {
+ instance = CreateObjectFromCode<Material>();
+ }
+
+ instance->SetNameCpp (Append (material->GetName (), " (Instance)"));
+ instance->m_Shader = material->m_Shader;
+ instance->m_Owner = &renderer;
+ if (material->m_Properties)
+ {
+ SET_ALLOC_OWNER(instance);
+ SAFE_RELEASE_LABEL(instance->m_Properties, kMemShader);
+ instance->m_Properties = UNITY_NEW(ShaderLab::PropertySheet (*material->m_Properties), kMemShader);
+ instance->m_Properties->SetOwnerMaterial(instance);
+ }
+ instance->m_CustomRenderQueue = material->m_CustomRenderQueue;
+ instance->m_SavedProperties = material->m_SavedProperties;
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1))
+ {
+ instance->m_ShaderKeywords = material->m_ShaderKeywords;
+ instance->m_ShaderKeywordSet = material->m_ShaderKeywordSet;
+ }
+ instance->m_ShadowCollectorHash = material->m_ShadowCollectorHash;
+ instance->m_ShadowCasterHash = material->m_ShadowCasterHash;
+ instance->m_StateKeyHash = material->m_StateKeyHash;
+ if( instance->m_Shader )
+ instance->m_Shader->AddMaterialUser( instance->m_ShaderUserNode );
+ return *instance;
+ }
+}
+
+
+
+#if UNITY_EDITOR
+inline void EmitWarningAboutSettingBuiltinParam(const char* name)
+{
+ static const std::string kWarningPrefix = std::string("Trying to set builtin parameter \"");
+ static const std::string kWarningSuffix = std::string("\". Will be ignored.");
+
+ WarningString( kWarningPrefix + name + kWarningSuffix );
+}
+#endif
+
+
+void Material::SetColor (ShaderLab::FastPropertyName name, const ColorRGBAf &col)
+{
+#if UNITY_EDITOR
+ if( IsVectorBuiltinParam(name.GetName()) )
+ EmitWarningAboutSettingBuiltinParam(name.GetName());
+#endif
+
+ UnityPropertySheet::ColorMap::iterator i = m_SavedProperties.m_Colors.find (name);
+ if( i != m_SavedProperties.m_Colors.end() )
+ {
+ #if UNITY_EDITOR
+ if (i->second.NotEquals(col))
+ #endif
+ {
+ i->second = col;
+ SetDirty();
+ }
+ }
+
+ ShaderLab::PropertySheet& properties = GetWritableProperties();
+ if (properties.GetColorTag(name))
+ properties.SetVector (name, GammaToActiveColorSpace(col).GetPtr());
+ else
+ properties.SetVector (name, col.GetPtr());
+
+ UpdateHashesOnPropertyChange (name);
+}
+
+
+void Material::SetColorIndexed (ShaderLab::FastPropertyName name, int index, float value)
+{
+#if UNITY_EDITOR
+ if( IsVectorBuiltinParam(name.GetName()) )
+ EmitWarningAboutSettingBuiltinParam(name.GetName());
+#endif
+
+ UnityPropertySheet::ColorMap::iterator i = m_SavedProperties.m_Colors.find (name);
+ if( i != m_SavedProperties.m_Colors.end() )
+ {
+ #if UNITY_EDITOR
+ if (i->second.GetPtr()[index] != value)
+ #endif
+ {
+ i->second.GetPtr()[index] = value;
+ SetDirty();
+ }
+ }
+
+ ShaderLab::PropertySheet& properties = GetWritableProperties();
+ if (properties.GetColorTag(name))
+ properties.SetVectorIndexed (name, index, GammaToActiveColorSpace(value));
+ else
+ properties.SetVectorIndexed (name, index, value);
+
+ UpdateHashesOnPropertyChange (name);
+}
+
+ColorRGBAf Material::GetColor (ShaderLab::FastPropertyName name)
+{
+ EnsurePropertiesExist ();
+
+ const Vector4f* prop = m_Properties->FindVector(name);
+ if( prop == NULL ) {
+ AssertStringObject (Format ("Material doesn't have a color property '%s'", name.GetName()), this);
+ return ColorRGBAf (0,0,0,0);
+ }
+
+ ColorRGBAf color (prop->x, prop->y, prop->z, prop->w);
+ if (m_Properties->GetColorTag(name))
+ return ActiveToGammaColorSpace (color);
+ else
+ return color;
+}
+
+
+void Material::SetFloat (ShaderLab::FastPropertyName name, float val)
+{
+#if UNITY_EDITOR
+ if( IsVectorBuiltinParam(name.GetName()) )
+ EmitWarningAboutSettingBuiltinParam(name.GetName());
+#endif
+
+ UnityPropertySheet::FloatMap::iterator i = m_SavedProperties.m_Floats.find (name);
+ if( i != m_SavedProperties.m_Floats.end() )
+ {
+ #if UNITY_EDITOR
+ if (i->second != val)
+ #endif
+ {
+ i->second = val;
+ SetDirty();
+ }
+ }
+ GetWritableProperties().SetFloat (name, val);
+
+ UpdateHashesOnPropertyChange (name);
+}
+
+float Material::GetFloat (ShaderLab::FastPropertyName name)
+{
+ EnsurePropertiesExist ();
+
+ const float* prop = m_Properties->FindFloat(name);
+ if( prop == NULL ) {
+ AssertStringObject (Format ("Material doesn't have a float or range property '%s'", name.GetName()), this);
+ return 0;
+ }
+ return *prop;
+}
+
+bool Material::HasProperty (ShaderLab::FastPropertyName name)
+{
+ EnsurePropertiesExist ();
+ return m_Properties->HasProperty(name);
+}
+
+
+void Material::ApplyMaterialPropertyDrawers ()
+{
+# if UNITY_EDITOR
+ // Don't try to apply property drawers for the very initial Reset()
+ // that happens; the shader isn't set up yet anyway.
+ if (!m_Shader.IsValid())
+ return;
+ if (!GetMonoManagerPtr())
+ return;
+
+ void* params[] = {Scripting::ScriptingWrapperFor(this)};
+ CallStaticMonoMethod ("MaterialEditor", "ApplyMaterialPropertyDrawers", params);
+# endif // if UNITY_EDITOR
+}
+
+
+#if UNITY_EDITOR
+bool Material::ActuallyHasTextureProperty (ShaderLab::FastPropertyName name) const
+{
+ return m_SavedProperties.m_TexEnvs.find (name) != m_SavedProperties.m_TexEnvs.end();
+}
+
+
+void Material::ResetDefaultTextures(bool overrideSetTextures)
+{
+ if( !m_Shader )
+ return;
+
+ const Shader::DefaultTexturesMap& defaultTextures = m_Shader->GetDefaultTextures();
+ for (Shader::DefaultTexturesMap::const_iterator it = defaultTextures.begin(); it != defaultTextures.end(); ++it)
+ {
+ bool nullDefaultTexture = it->second.IsNull();
+
+ for (int i = 0; i < m_Shader->GetPropertyCount (); i++)
+ {
+ const ShaderLab::ParserProperty* shaderProperty = m_Shader->GetPropertyInfo (i);
+
+ // only assign if it is a texture where the name and dimensions match
+ if (shaderProperty->m_Name != it->first.c_str()
+ || shaderProperty->m_Type != ShaderLab::ParserProperty::kTexture
+ || (!nullDefaultTexture && shaderProperty->m_DefTexture.m_TexDim != it->second->GetDimension ()))
+ continue;
+
+ if (!overrideSetTextures)
+ {
+ if (GetTexture(ShaderLab::Property(it->first)) != NULL)
+ continue;
+ }
+
+ SetTexture (ShaderLab::Property(it->first), it->second);
+ break;
+ }
+ }
+}
+#endif
+
+
+// Get/Set a matrix value in the material
+// We don't allow user editing of matrix props, hence they don't have a representation in the
+// UnityPropertySheet, hence we work directly with the SL properties here
+void Material::SetMatrix (ShaderLab::FastPropertyName name, const Matrix4x4f &val)
+{
+#if UNITY_EDITOR
+ if( IsMatrixBuiltinParam(name.GetName()) )
+ EmitWarningAboutSettingBuiltinParam(name.GetName());
+#endif
+
+ GetWritableProperties().SetValueProp (name, 16, val.GetPtr());
+
+ UpdateHashesOnPropertyChange (name);
+}
+
+const Matrix4x4f &Material::GetMatrix (ShaderLab::FastPropertyName name)
+{
+ EnsurePropertiesExist ();
+
+ int count = 0;
+ const float* prop = m_Properties->GetValueProp (name, &count);
+ if (prop == NULL || count != 16)
+ {
+ AssertStringObject (Format ("Material doesn't have a matrix property '%s'", name.GetName()), this);
+ return Matrix4x4f::identity;
+ }
+ return *reinterpret_cast<const Matrix4x4f*>(prop);
+}
+
+
+void Material::SetTexture (ShaderLab::FastPropertyName name, Texture *val)
+{
+ UnityPropertySheet::TexEnvMap::iterator i = m_SavedProperties.m_TexEnvs.find (name);
+ if( i != m_SavedProperties.m_TexEnvs.end() )
+ {
+ #if UNITY_EDITOR
+ if (i->second.m_Texture != PPtr<Texture>(val))
+ #endif
+ {
+ i->second.m_Texture = val;
+ SetDirty();
+ }
+ }
+ else
+ {
+ //AssertStringObject (Format ("Material '%s' doesn't have a texture property '%s'", GetName(), name.GetName()), this);
+ }
+
+ GetWritableProperties().SetTexture (name, val);
+
+ UpdateHashesOnPropertyChange (name);
+}
+
+
+Texture *Material::GetTexture (ShaderLab::FastPropertyName name)
+{
+ EnsurePropertiesExist ();
+
+ UnityPropertySheet::TexEnvMap::const_iterator i = m_SavedProperties.m_TexEnvs.find (name);
+ if (i == m_SavedProperties.m_TexEnvs.end())
+ {
+ AssertStringObject (Format ("Material doesn't have a texture property '%s'", name.GetName()), this);
+ return 0;
+ }
+ return PPtr<Texture> (i->second.m_Texture);
+}
+
+void Material::SetTextureOffset( ShaderLab::FastPropertyName name, const Vector2f& val )
+{
+ UnityPropertySheet::TexEnvMap::iterator i = m_SavedProperties.m_TexEnvs.find (name);
+ if( i != m_SavedProperties.m_TexEnvs.end() )
+ {
+ #if UNITY_EDITOR
+ if (i->second.m_Offset != val)
+ #endif
+ {
+ i->second.m_Offset.Set( val.x, val.y );
+ SetDirty();
+ }
+ }
+ GetWritableProperties().SetTextureOffset( name, val.x, val.y );
+
+ UpdateHashesOnPropertyChange (name);
+}
+
+void Material::SetTextureScale( ShaderLab::FastPropertyName name, const Vector2f& val )
+{
+ UnityPropertySheet::TexEnvMap::iterator i = m_SavedProperties.m_TexEnvs.find (name);
+ if( i != m_SavedProperties.m_TexEnvs.end() )
+ {
+ #if UNITY_EDITOR
+ if (i->second.m_Scale != val)
+ #endif
+ {
+ i->second.m_Scale.Set( val.x, val.y );
+ SetDirty();
+ }
+ }
+ GetWritableProperties().SetTextureScale( name, val.x, val.y );
+
+ UpdateHashesOnPropertyChange (name);
+}
+
+void Material::SetTextureScaleAndOffsetIndexed( ShaderLab::FastPropertyName name, int index, float value)
+{
+ UnityPropertySheet::TexEnvMap::iterator i = m_SavedProperties.m_TexEnvs.find (name);
+ if( i != m_SavedProperties.m_TexEnvs.end() )
+ {
+ if (index < 2)
+ {
+ i->second.m_Scale[index] = value;
+ }
+ else
+ {
+ i->second.m_Offset[index - 2] = value;
+ }
+ SetDirty();
+ }
+ GetWritableProperties().SetTextureScaleAndOffsetIndexed( name, index, value );
+
+ UpdateHashesOnPropertyChange (name);
+}
+
+Vector2f Material::GetTextureOffset( ShaderLab::FastPropertyName name )
+{
+ // get from current runtime sheet
+ const ShaderLab::TexEnv* texEnv = GetProperties().GetTexEnv (name);
+ if (texEnv != NULL)
+ return texEnv->GetTextureOffset ();
+
+ // if not there, get from serialized properties
+ UnityPropertySheet::TexEnvMap::const_iterator it = m_SavedProperties.m_TexEnvs.find (name);
+ if (it != m_SavedProperties.m_TexEnvs.end())
+ return it->second.m_Offset;
+
+ AssertStringObject (Format ("Material doesn't have a texture property '%s'", name.GetName()), this);
+ return Vector2f(0,0);
+}
+
+Vector2f Material::GetTextureScale( ShaderLab::FastPropertyName name )
+{
+ // get from current runtime sheet
+ const ShaderLab::TexEnv* texEnv = GetProperties().GetTexEnv (name);
+ if (texEnv != NULL)
+ return texEnv->GetTextureScale ();
+
+ // if not there, get from serialized properties
+ UnityPropertySheet::TexEnvMap::const_iterator it = m_SavedProperties.m_TexEnvs.find (name);
+ if (it != m_SavedProperties.m_TexEnvs.end())
+ return it->second.m_Scale;
+
+ AssertStringObject (Format ("Material doesn't have a texture property '%s'", name.GetName()), this);
+ return Vector2f(1,1);
+}
+
+void Material::SetComputeBuffer (ShaderLab::FastPropertyName name, ComputeBufferID val)
+{
+ GetWritableProperties().SetComputeBuffer (name, val);
+}
+
+void Material::CopyPropertiesFromMaterial(Material& other)
+{
+ m_SavedProperties = other.GetSavedProperties();
+ SAFE_RELEASE_LABEL(m_Properties, kMemShader);
+ {
+ SET_ALLOC_OWNER(this);
+ m_Properties = UNITY_NEW(ShaderLab::PropertySheet(other.GetProperties()), kMemShader);
+ m_Properties->SetOwnerMaterial(this);
+ }
+ m_PropertiesDirty = true;
+
+ Shader *shader = m_Shader;
+ if( !shader )
+ shader = Shader::GetDefault();
+
+ UpdateHashes();
+}
+
+void Material::ClearProperties ()
+{
+ SAFE_RELEASE_LABEL(m_Properties, kMemShader);
+ m_PropertiesDirty = true;
+ m_ShaderUserNode.RemoveFromList();
+}
+
+int Material::GetRuntimeMemorySize() const
+{ int size = Super::GetRuntimeMemorySize();
+ if (m_Properties)
+ size += m_Properties->GetMemoryUsage();
+ return size;
+}
+
+void Material::InvalidateDisplayLists()
+{
+ int subShaderCount = m_CachedSubShaders.size();
+ for (int i = 0; i < subShaderCount; i++)
+ {
+ CachedSubShader& subshader = m_CachedSubShaders[i];
+ int passCount = subshader.passes.size();
+ for (int j = 0; j < passCount; j++)
+ {
+ CachedShaderPass& pass = subshader.passes[j];
+ SAFE_RELEASE(pass.displayList);
+ }
+ SAFE_RELEASE(subshader.shadowCasterPass.displayList);
+ SAFE_RELEASE(subshader.shadowCollectorPass.displayList);
+ }
+ m_PropertiesDirty = false;
+}
+
+void Material::BuildShaderKeywordSet ()
+{
+ m_ShaderKeywordSet.Reset ();
+ for (size_t q = 0; q < m_ShaderKeywords.size (); ++q)
+ m_ShaderKeywordSet.Enable (keywords::Create (m_ShaderKeywords[q]));
+}
+
+void Material::SetShaderKeywords (const ShaderKeywordsT& keywords)
+{
+ m_ShaderKeywords = keywords;
+ BuildShaderKeywordSet ();
+ UpdateHashes();
+ SetDirty();
+}
+
+void Material::EnableKeyword (const std::string& key)
+{
+ // already set -> do nothing
+ if (std::find(m_ShaderKeywords.begin(), m_ShaderKeywords.end(), key) != m_ShaderKeywords.end())
+ return;
+ m_ShaderKeywords.push_back (key);
+ BuildShaderKeywordSet ();
+ UpdateHashes();
+ SetDirty();
+}
+
+void Material::DisableKeyword (const std::string& key)
+{
+ // no keyword -> do nothing
+ ShaderKeywordsT::iterator it = std::find(m_ShaderKeywords.begin(), m_ShaderKeywords.end(), key);
+ if (it == m_ShaderKeywords.end())
+ return;
+
+ m_ShaderKeywords.erase (it);
+ BuildShaderKeywordSet ();
+ UpdateHashes();
+ SetDirty();
+}
+
+
+}
+
+IMPLEMENT_CLASS (Material)
+IMPLEMENT_OBJECT_SERIALIZE (Material)
+INSTANTIATE_TEMPLATE_TRANSFER (Material)
diff --git a/Runtime/Shaders/Material.h b/Runtime/Shaders/Material.h
new file mode 100644
index 0000000..46dd954
--- /dev/null
+++ b/Runtime/Shaders/Material.h
@@ -0,0 +1,233 @@
+#ifndef MATERIAL_H
+#define MATERIAL_H
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Vector4.h"
+#include "Runtime/Math/Color.h"
+#include "UnityPropertySheet.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Shaders/ShaderKeywords.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Modules/ExportModules.h"
+
+namespace ShaderLab
+{
+ class PropertySheet;
+ struct FastPropertyName;
+ class ShaderState;
+ class Pass;
+}
+
+class ChannelAssigns;
+class Shader;
+class Texture;
+class Matrix4x4f;
+class GfxDisplayList;
+
+
+
+namespace Unity
+{
+
+class EXPORT_COREMODULE Material : public NamedObject
+{
+ public:
+ REGISTER_DERIVED_CLASS (Material, NamedObject)
+ DECLARE_OBJECT_SERIALIZE (Material)
+
+ Material (MemLabelId label, ObjectCreationMode mode);
+ // ~Material (); declared-by-macro
+ virtual void Reset();
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ /// Get how many passes this material requires to render
+ int GetPassCount ();
+
+ // Set the pass to render next time.
+ // Calling this function sets up the shader.
+ // Returns vertex channels used; NULL if this pass should not be rendered.
+ const ChannelAssigns* SetPass (int passNo, int subshaderIndex = 0, bool allowRecording = true);
+
+ // Set the pass to render next time.
+ // Can supply a different shader from the one in material, or just cached PPtr deref.
+ // Also indicates which subshader to use.
+ // Calling this function sets up the shader.
+ // Returns vertex channels used; NULL if this pass should not be rendered.
+ const ChannelAssigns* SetPassWithShader( int passNo, Shader* shader, int subshaderIndex );
+
+ const ChannelAssigns* SetShadowCasterPass(int subshaderIndex);
+ const ChannelAssigns* SetShadowCasterPassWithShader(Shader* shader, int subshaderIndex);
+ const ChannelAssigns* SetShadowCollectorPass(int subshaderIndex);
+ const ChannelAssigns* SetShadowCollectorPassWithShader(Shader* shader, int subshaderIndex);
+
+ int GetActualRenderQueue() const;
+ int GetCustomRenderQueue() const { return m_CustomRenderQueue; }
+ void SetCustomRenderQueue (int q) { if (m_CustomRenderQueue != q) { m_CustomRenderQueue = q; SetDirty(); } }
+
+ inline const ShaderLab::PropertySheet& GetProperties ()
+ {
+ EnsurePropertiesExist ();
+ return *m_Properties;
+ }
+
+ inline void EnsurePropertiesExist ()
+ {
+// #if UNITY_EDITOR || WEBPLUG
+ #if 1
+ // Don't preload in the editor because it can easily run out of memory and increase build times.
+ // In the webplayer we also have some hacks where we call ClearProperties that must be fixed before we can remove the WEBPLUG condition
+ if( !m_Properties )
+ BuildProperties();
+ #else
+ // Enforce that everything is preloaded in the player, reduces hiccups at runtime.
+ Assert(m_Properties != NULL);
+ #endif
+ }
+
+ inline ShaderLab::PropertySheet& GetWritableProperties ()
+ {
+ EnsurePropertiesExist ();
+ SetPropertiesDirty();
+ return *m_Properties;
+ }
+
+ void SetPropertiesDirty ()
+ {
+ m_PropertiesDirty = true;
+ }
+
+ /// Clears the cached properties.
+ /// This is used in the web player to avoid caching issues with builtin resources when reloading the player.
+ void ClearProperties ();
+
+ // Sets the materials properties
+ void CopyPropertiesFromMaterial(Material& other);
+
+ // Get/Set a color value in the material
+ void SetColor (ShaderLab::FastPropertyName name, const ColorRGBAf &col);
+ void SetColorIndexed (ShaderLab::FastPropertyName name, int indexed, float value);
+ ColorRGBAf GetColor (ShaderLab::FastPropertyName name);
+
+ // Get/Set a float value in the material
+ void SetFloat (ShaderLab::FastPropertyName name, float val);
+ float GetFloat (ShaderLab::FastPropertyName name);
+
+ // Get/Set a matrix value in the material
+ void SetMatrix ( ShaderLab::FastPropertyName name, const Matrix4x4f &val);
+ const Matrix4x4f &GetMatrix (ShaderLab::FastPropertyName name);
+
+ // Get/Set a texture value in the material
+ void SetTexture (ShaderLab::FastPropertyName name, Texture *val);
+ Texture *GetTexture (ShaderLab::FastPropertyName name);
+
+ bool HasProperty (ShaderLab::FastPropertyName name);
+
+ // Texture placement
+ void SetTextureOffset( ShaderLab::FastPropertyName name, const Vector2f& offset );
+ void SetTextureScale( ShaderLab::FastPropertyName name, const Vector2f& scale );
+
+ Vector2f GetTextureOffset( ShaderLab::FastPropertyName name );
+ Vector2f GetTextureScale( ShaderLab::FastPropertyName name );
+
+ void SetTextureScaleAndOffsetIndexed (ShaderLab::FastPropertyName name, int indexed, float value);
+
+ void SetComputeBuffer (ShaderLab::FastPropertyName name, ComputeBufferID val);
+
+
+ // Get the default material
+ static Material *GetDefault ();
+ static Material *GetDefaultDiffuseMaterial ();
+
+ static Material *CreateMaterial (const char *shaderStr, int hideFlags, bool scriptingObjectIsBeingCreated = false);
+ static Material *CreateMaterial (Shader& shader, int hideFlags, bool scriptingObjectIsBeingCreated = false);
+ static Material *CreateMaterial (const Material& material, int hideFlags, bool scriptingObjectIsBeingCreated = false);
+
+ // Set the shader that drives the material
+ void SetShader (Shader *s);
+
+ const Shader *GetShader() const;
+ Shader *GetShader();
+ PPtr<Shader> GetShaderPPtr() const;
+
+ std::string GetTag( const string& tag, bool currentSubShaderOnly, const string& defaultValue ) const;
+
+ typedef std::vector<UnityStr> ShaderKeywordsT;
+ const ShaderKeywordsT& GetShaderKeywords () const { return m_ShaderKeywords; }
+ void SetShaderKeywords (const ShaderKeywordsT& keywords);
+ ShaderKeywordSet GetShaderKeywordSet () const { return m_ShaderKeywordSet; }
+ void EnableKeyword (const std::string& key);
+ void DisableKeyword (const std::string& key);
+ void ApplyMaterialPropertyDrawers();
+
+ #if UNITY_EDITOR
+ bool ActuallyHasTextureProperty (ShaderLab::FastPropertyName name) const;
+
+ void ResetDefaultTextures (bool overrideSetTextures);
+ #endif
+
+ inline UnityPropertySheet& GetSavedProperties () { return m_SavedProperties; }
+ inline const UnityPropertySheet& GetSavedProperties () const { return m_SavedProperties; }
+
+ // If m_Owner equals renderer returns this
+ // Otherwise creates a copy and sets m_Owner to renderer.
+ // Used by Animation system and scripting to animate material properties of a single renderer.
+ static Material& GetInstantiatedMaterial (Material* material, Object& renderer, bool allowInEditMode);
+ PPtr<Object> GetOwner () { return m_Owner; }
+
+ virtual int GetRuntimeMemorySize () const;
+
+ void InvalidateDisplayLists ();
+
+ inline UInt32 GetShadowCasterHash() { EnsurePropertiesExist (); return m_ShadowCasterHash; }
+ inline UInt32 GetShadowCollectorHash() { EnsurePropertiesExist (); return m_ShadowCollectorHash; }
+
+private:
+ /// Build the ShaderLab property sheet (m_Properties) from the saved properties.
+ /// This will correctly handle default properties supplied by the shader...
+ void BuildProperties ();
+ void BuildShaderKeywordSet ();
+
+ void UpdateHashesOnPropertyChange (ShaderLab::FastPropertyName name);
+ void UpdateHashes ();
+
+ struct CachedShaderPass
+ {
+ CachedShaderPass() : displayList(NULL), channelAssigns(NULL), shaderKeywords(0), globalFogMode(kFogDisabled) {}
+
+ GfxDisplayList* displayList;
+ const ChannelAssigns* channelAssigns;
+ UInt64 shaderKeywords;
+ FogMode globalFogMode;
+ };
+
+ struct CachedSubShader
+ {
+ dynamic_array<CachedShaderPass> passes;
+ CachedShaderPass shadowCasterPass;
+ CachedShaderPass shadowCollectorPass;
+ };
+
+ PPtr<Shader> m_Shader;
+ ShaderLab::PropertySheet* m_Properties;
+ bool m_PropertiesDirty;
+ std::vector<CachedSubShader> m_CachedSubShaders;
+ int m_CustomRenderQueue; // -1 if should use shader's
+ PPtr<Object> m_Owner;
+ UnityPropertySheet m_SavedProperties;
+
+ ListNode<Material> m_ShaderUserNode;
+ ShaderKeywordsT m_ShaderKeywords;
+ ShaderKeywordSet m_ShaderKeywordSet;
+ UInt32 m_ShadowCollectorHash;
+ UInt32 m_ShadowCasterHash;
+ UInt32 m_StateKeyHash;
+};
+
+}
+
+using namespace Unity;
+
+#endif
diff --git a/Runtime/Shaders/MaterialIsTransparent.h b/Runtime/Shaders/MaterialIsTransparent.h
new file mode 100644
index 0000000..072b054
--- /dev/null
+++ b/Runtime/Shaders/MaterialIsTransparent.h
@@ -0,0 +1,14 @@
+#include "Material.h"
+
+inline bool IsTransparentOrCutoutMaterial (Material& material)
+{
+ string tag = material.GetTag("RenderType", false, string());
+
+ if (material.GetActualRenderQueue() >= kAlphaTestRenderQueue)
+ return true;
+
+ if (tag == "TreeTransparentCutout" || tag == "GrassBillboard" || tag == "Grass" || tag == "TreeLeaf" || tag == "TranparentCutout" || tag == "Tranparent")
+ return true;
+
+ return false;
+}
diff --git a/Runtime/Shaders/MaterialProperties.cpp b/Runtime/Shaders/MaterialProperties.cpp
new file mode 100644
index 0000000..357a73f
--- /dev/null
+++ b/Runtime/Shaders/MaterialProperties.cpp
@@ -0,0 +1,195 @@
+#include "UnityPrefix.h"
+#include "MaterialProperties.h"
+#include "Material.h"
+#include "External/shaderlab/Library/properties.h"
+#include "Runtime/Math/Matrix4x4.h"
+
+inline size_t CalculateSizeFromProperty (const MaterialPropertyBlock::Property& prop)
+{
+ return prop.rows * prop.cols * prop.arraySize;
+}
+
+MaterialPropertyBlock::MaterialPropertyBlock(Property* props, size_t propCount, float* buffer, size_t bufSize)
+{
+ m_Properties.assign_external(props, props + propCount);
+ m_Buffer.assign_external(buffer, buffer + bufSize);
+}
+
+void MaterialPropertyBlock::AddProperty(const ShaderLab::FastPropertyName& name, const float* data, UInt8 rows, UInt8 cols, size_t arraySize)
+{
+ size_t offset = m_Buffer.size();
+ Property prop = { name.index, rows, cols, kTexDimNone, arraySize, offset };
+ m_Properties.push_back(prop);
+ size_t size = rows * cols * arraySize;
+ m_Buffer.resize_uninitialized(offset + size);
+ memcpy(&m_Buffer[offset], data, size * sizeof(float));
+}
+
+void MaterialPropertyBlock::AddPropertyTexture(const ShaderLab::FastPropertyName& name, TextureDimension dim, TextureID tid)
+{
+ AddProperty (name, reinterpret_cast<float*> (&tid.m_ID), 1, 1, 1);
+ m_Properties.back().texDim = dim;
+}
+
+void MaterialPropertyBlock::AddPropertyFloat(const ShaderLab::FastPropertyName& name, float val)
+{
+ AddProperty(name, &val, 1, 1, 1);
+}
+
+void MaterialPropertyBlock::AddPropertyVector(const ShaderLab::FastPropertyName& name, const Vector4f& vec)
+{
+ AddProperty(name, vec.GetPtr(), 1, 4, 1);
+}
+
+void MaterialPropertyBlock::AddPropertyColor(const ShaderLab::FastPropertyName& name, const ColorRGBAf& col)
+{
+ ColorRGBAf converted = GammaToActiveColorSpace (col);
+ AddProperty(name, converted.GetPtr(), 1, 4, 1);
+}
+
+void MaterialPropertyBlock::AddPropertyMatrix(const ShaderLab::FastPropertyName& name, const Matrix4x4f& mat)
+{
+ AddProperty(name, mat.GetPtr(), 4, 4, 1);
+}
+
+void MaterialPropertyBlock::ReplacePropertyTexture(const ShaderLab::FastPropertyName& name, TextureDimension dim, TextureID tid)
+{
+ int index = GetPropertyIndex(name);
+ if (index == -1)
+ AddPropertyTexture(name, dim, tid);
+ else
+ {
+ Property& prop = m_Properties[index];
+ if (prop.rows == 1 && prop.cols == 1 && prop.arraySize == 1)
+ {
+ TextureID* buf = reinterpret_cast<TextureID*> (&m_Buffer[prop.offset]);
+ *buf = tid;
+ prop.texDim = dim;
+ }
+ else
+ {
+ ErrorString("The material property is different from already stored property.");
+ }
+ }
+}
+
+void MaterialPropertyBlock::ReplacePropertyColor(const ShaderLab::FastPropertyName& name, const ColorRGBAf& col)
+{
+ ColorRGBAf activeColor = GammaToActiveColorSpace (col);
+ ReplacePropertyVector (name, *reinterpret_cast<const Vector4f*> (&activeColor));
+}
+
+void MaterialPropertyBlock::ReplacePropertyVector(const ShaderLab::FastPropertyName& name, const Vector4f& vec)
+{
+ int index = GetPropertyIndex(name);
+ if (index == -1)
+ AddPropertyVector(name, vec);
+ else
+ {
+ const Property& prop = m_Properties[index];
+ if (prop.rows == 1 && prop.cols == 4 && prop.arraySize == 1)
+ {
+ Vector4f* buf = reinterpret_cast<Vector4f*> (&m_Buffer[prop.offset]);
+ *buf = vec;
+ }
+ else
+ {
+ ErrorString("The material property is different from already stored property.");
+ }
+ }
+}
+
+void MaterialPropertyBlock::ReplacePropertyFloat(const ShaderLab::FastPropertyName& name, float data)
+{
+ ReplacePartialFloatProperty(name, data, 1, 0);
+}
+
+void MaterialPropertyBlock::ReplacePartialFloatColorProperty(const ShaderLab::FastPropertyName& name, float data, UInt8 cols, UInt8 colIndex)
+{
+ ReplacePartialFloatProperty(name, GammaToActiveColorSpace(data), cols, colIndex);
+}
+
+void MaterialPropertyBlock::ReplacePartialFloatProperty(const ShaderLab::FastPropertyName& name, float data, UInt8 cols, UInt8 colIndex)
+{
+ int index = GetPropertyIndex(name);
+ if (index == -1)
+ {
+ float prop[4] = { 0.0F, 0.0F, 0.0F, 0.0F };
+ prop[colIndex] = data;
+
+ AddProperty(name, prop, 1, cols, 1);
+ }
+ else
+ {
+ const Property& prop = m_Properties[index];
+ if (prop.rows == 1 && prop.cols == cols && prop.arraySize == 1)
+ {
+ float* buffer = reinterpret_cast<float*> (&m_Buffer[prop.offset]);
+ buffer[colIndex] = data;
+ }
+ else
+ {
+ ErrorString("The material property is different from already stored property.");
+ }
+ }
+}
+
+int MaterialPropertyBlock::GetPropertyIndex (const ShaderLab::FastPropertyName& name) const
+{
+ for (int i=0;i<m_Properties.size();i++)
+ {
+ if (m_Properties[i].nameIndex == name.index)
+ return i;
+ }
+ return -1;
+}
+
+const void* MaterialPropertyBlock::Find(const ShaderLab::FastPropertyName& name, UInt8 rows, UInt8 cols, size_t arraySize) const
+{
+ for (size_t i = 0, n = m_Properties.size(); i != n; ++i)
+ {
+ const Property& prop = m_Properties[i];
+ if (name.index == prop.nameIndex && prop.cols == cols && prop.rows == rows)
+ return &m_Buffer[prop.offset];
+ }
+ return NULL;
+}
+
+
+const float* MaterialPropertyBlock::FindFloat(const ShaderLab::FastPropertyName& name) const
+{
+ return static_cast<const float*> (Find(name, 1, 1, 1));
+}
+
+const Vector4f* MaterialPropertyBlock::FindVector(const ShaderLab::FastPropertyName& name) const
+{
+ return static_cast<const Vector4f*> (Find(name, 1, 4, 1));
+}
+
+const Matrix4x4f* MaterialPropertyBlock::FindMatrix(const ShaderLab::FastPropertyName& name) const
+{
+ return static_cast<const Matrix4x4f*> (Find(name, 4, 4, 1));
+}
+
+bool MaterialPropertyBlock::GetColor(const ShaderLab::FastPropertyName& name, ColorRGBAf& outColor) const
+{
+ const ColorRGBAf* color = static_cast<const ColorRGBAf*> (Find(name, 1, 4, 1));
+ if (color != NULL)
+ {
+ outColor = ActiveToGammaColorSpace(*color);
+ return true;
+ }
+ else
+ return false;
+}
+
+const TextureID MaterialPropertyBlock::FindTexture(const ShaderLab::FastPropertyName& name) const
+{
+ for (size_t i = 0, n = m_Properties.size(); i != n; ++i)
+ {
+ const Property& prop = m_Properties[i];
+ if (name.index == prop.nameIndex && prop.texDim != kTexDimNone)
+ return TextureID(*(const int*)&m_Buffer[prop.offset]);
+ }
+ return TextureID();
+}
diff --git a/Runtime/Shaders/MaterialProperties.h b/Runtime/Shaders/MaterialProperties.h
new file mode 100644
index 0000000..5c1c0bd
--- /dev/null
+++ b/Runtime/Shaders/MaterialProperties.h
@@ -0,0 +1,85 @@
+#ifndef MATERIAL_PROPERTIES_H
+#define MATERIAL_PROPERTIES_H
+
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Modules/ExportModules.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+class Vector4f;
+class Matrix4x4f;
+class ColorRGBAf;
+namespace Unity { class Material; }
+namespace ShaderLab { struct FastPropertyName; }
+
+// Tightly packed buffer of material properties
+class EXPORT_COREMODULE MaterialPropertyBlock
+{
+public:
+ struct Property
+ {
+ int nameIndex;
+ UInt8 rows;
+ UInt8 cols;
+ UInt8 texDim; // if texDim==None, this is a value property
+ // These should not be size_t, as the GfxDevice may run across processes of different
+ // bitness, and the data serialized in the command buffer must match.
+ UInt32 arraySize;
+ UInt32 offset;
+ };
+
+ MaterialPropertyBlock() {}
+
+ // Does not copy data!
+ MaterialPropertyBlock(Property* props, size_t propCount, float* buffer, size_t bufSize);
+
+ // Clear all properties
+ void Clear();
+
+ // Add Properties without checking if another property with the same name exists
+ void AddProperty(const ShaderLab::FastPropertyName& name, const float* data, UInt8 rows, UInt8 cols, size_t arraySize);
+ void AddPropertyFloat(const ShaderLab::FastPropertyName& name, float val);
+ void AddPropertyVector(const ShaderLab::FastPropertyName& name, const Vector4f& vec);
+ void AddPropertyColor(const ShaderLab::FastPropertyName& name, const ColorRGBAf& col);
+ void AddPropertyMatrix(const ShaderLab::FastPropertyName& name, const Matrix4x4f& mat);
+ void AddPropertyTexture(const ShaderLab::FastPropertyName& name, TextureDimension dim, TextureID tid);
+
+ // Replace properties
+ void ReplacePropertyFloat(const ShaderLab::FastPropertyName& name, float data);
+ void ReplacePropertyVector(const ShaderLab::FastPropertyName& name, const Vector4f& col);
+ void ReplacePropertyColor(const ShaderLab::FastPropertyName& name, const ColorRGBAf& col);
+ void ReplacePropertyTexture(const ShaderLab::FastPropertyName& name, TextureDimension dim, TextureID tid);
+
+ // Replaces a single float property on either a float1 or one component of a float4.
+ /// If other components on a float4 are not yet defined, they will be initialized to zero
+ void ReplacePartialFloatProperty(const ShaderLab::FastPropertyName& name, float data, UInt8 cols, UInt8 colIndex);
+ void ReplacePartialFloatColorProperty(const ShaderLab::FastPropertyName& name, float data, UInt8 cols, UInt8 colIndex);
+
+
+ const float* FindFloat(const ShaderLab::FastPropertyName& name) const;
+ const Vector4f* FindVector(const ShaderLab::FastPropertyName& name) const;
+ bool GetColor(const ShaderLab::FastPropertyName& name, ColorRGBAf& outColor) const;
+ const Matrix4x4f* FindMatrix(const ShaderLab::FastPropertyName& name) const;
+ const TextureID FindTexture(const ShaderLab::FastPropertyName& name) const;
+
+ const Property* GetPropertiesBegin() const { return m_Properties.begin(); }
+ const Property* GetPropertiesEnd() const { return m_Properties.end(); }
+ const float* GetBufferBegin() const { return m_Buffer.begin(); }
+ const float* GetBufferEnd() const { return m_Buffer.end(); }
+
+
+ const void* Find(const ShaderLab::FastPropertyName& name, UInt8 rows, UInt8 cols, size_t arraySize) const;
+ int GetPropertyIndex (const ShaderLab::FastPropertyName& name) const;
+
+private:
+ dynamic_array<Property> m_Properties;
+ dynamic_array<float> m_Buffer;
+};
+
+inline void MaterialPropertyBlock::Clear()
+{
+ m_Properties.resize_uninitialized(0);
+ m_Buffer.resize_uninitialized(0);
+}
+
+
+#endif
diff --git a/Runtime/Shaders/NameToObjectMap.h b/Runtime/Shaders/NameToObjectMap.h
new file mode 100644
index 0000000..6c63f22
--- /dev/null
+++ b/Runtime/Shaders/NameToObjectMap.h
@@ -0,0 +1,153 @@
+#ifndef NAMETOOBJECTMAP_H
+#define NAMETOOBJECTMAP_H
+
+#include <string>
+#include <map>
+#include "Runtime/BaseClasses/BaseObject.h"
+#if UNITY_EDITOR
+#include "Editor/Src/BuildPipeline/BuildSerialization.h"
+#endif
+
+using std::map;
+using std::string;
+using std::pair;
+
+
+static inline bool IsBuiltinResourceObject(Object* o)
+{
+ // built-in resources have this flag set
+ if (o->TestHideFlag(Object::kHideAndDontSave))
+ return true;
+
+ // Resources from builtin_extra only have "not editable" flag set, so the above check doesn't catch them.
+ // In the editor (where this matters mostly when building resource files), catch that by detecting
+ // if the object came from any built-in resources file.
+ #if UNITY_EDITOR
+ if (IsAnyDefaultResourcesObject(o->GetInstanceID()))
+ return true;
+ #endif
+
+ return false;
+}
+
+template<class Type, class ObjectToName, class NameToObject>
+class NameToObjectMap
+{
+private:
+ typedef typename ObjectToName::iterator ObjectToNameIterator;
+ typedef typename NameToObject::iterator NameToObjectIterator;
+
+ ObjectToName m_ObjectToName;
+ NameToObject m_NameToObject;
+ Object* m_ObjectToDirty;
+
+public:
+
+ DECLARE_SERIALIZE (NameToObjectMap)
+
+ void SetObjectToDirty (Object* dirty) { m_ObjectToDirty = dirty; }
+
+ void Add (const string& name, PPtr<Type> o)
+ {
+ Remove (o);
+ m_ObjectToName.insert (make_pair (o, name));
+ m_NameToObject.insert (make_pair (name, o));
+ AssertIf (m_NameToObject.size () != m_ObjectToName.size ());
+ m_ObjectToDirty->SetDirty ();
+ }
+
+ bool Remove (PPtr<Type> object)
+ {
+ AssertIf (m_NameToObject.size () != m_ObjectToName.size ());
+ int oldSize = m_NameToObject.size ();
+ {
+ pair<NameToObjectIterator, NameToObjectIterator> range = make_pair(m_NameToObject.begin(), m_NameToObject.end());
+
+ NameToObjectIterator i, next;
+ for (i=range.first;i!=range.second;i=next)
+ {
+ next = i; next++;
+ if (i->second == object)
+ {
+ m_NameToObject.erase (i);
+ }
+ }
+ }
+
+ {
+ pair<ObjectToNameIterator, ObjectToNameIterator> range;
+ range = m_ObjectToName.equal_range (object);
+ m_ObjectToName.erase(range.first, range.second);
+ }
+
+ m_ObjectToDirty->SetDirty ();
+ AssertIf (m_NameToObject.size () != m_ObjectToName.size ());
+ return oldSize != m_NameToObject.size ();
+ }
+
+ Type* Find (const string& name)
+ {
+ // Get all with name 'name'
+ pair<NameToObjectIterator, NameToObjectIterator> range;
+ range = m_NameToObject.equal_range (name);
+ NameToObjectIterator i, next;
+ Type* found = NULL;
+ // Then find the first that is loaded, those that can't be loaded
+ // are removed.
+ for (i=range.first;i!=range.second;i=next)
+ {
+ next = i; next++;
+ Type* o = i->second;
+
+ if (o)
+ {
+ // When there are two shaders one builtin resource and one normal shader
+ // Then we want the one in the project folder not the builtin one. So people can override shaders
+ // At some point we should try to get the ordering of shader includes better defined!
+ if (found && IsBuiltinResourceObject(o))
+ continue;
+
+ found = o;
+ }
+ }
+
+ return found;
+ }
+
+ std::vector<PPtr<Type> > GetAllObjects ()
+ {
+ std::vector<PPtr<Type> > objects;
+ for (NameToObjectIterator i=m_NameToObject.begin ();i!=m_NameToObject.end ();i++)
+ {
+ objects.push_back(i->second);
+ }
+ return objects;
+ }
+
+ const NameToObject& GetAll ()
+ {
+ return m_NameToObject;
+ }
+
+private:
+
+ void Rebuild ()
+ {
+ // Rebuild name -> object
+ m_NameToObject.clear ();
+ ObjectToNameIterator i;
+ for (i=m_ObjectToName.begin ();i != m_ObjectToName.end ();i++)
+ m_NameToObject.insert (make_pair (i->second, i->first));
+ }
+};
+
+template<class Type, class ObjectToName, class NameToObject>
+template<class TransferFunction>
+void NameToObjectMap<Type, ObjectToName, NameToObject>::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (m_ObjectToName);
+ if (transfer.IsReading ())
+ Rebuild ();
+}
+
+#endif
diff --git a/Runtime/Shaders/Shader.cpp b/Runtime/Shaders/Shader.cpp
new file mode 100644
index 0000000..b33aa00
--- /dev/null
+++ b/Runtime/Shaders/Shader.cpp
@@ -0,0 +1,734 @@
+#include "UnityPrefix.h"
+#include "Shader.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "Material.h"
+#include "ShaderNameRegistry.h"
+#include "Runtime/Utilities/Word.h"
+#include "External/shaderlab/Library/ShaderParser.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "GraphicsCaps.h"
+#include "External/shaderlab/Library/SLParserData.h"
+
+#if UNITY_EDITOR
+#include "External/shaderlab/Library/ShaderWriter.h"
+#endif
+#include "Runtime/Profiler/MemoryProfiler.h"
+
+using std::vector;
+
+// To get a precompiled version of specific platform shader
+// Create this shader in UnityEditor and click on "Open compiled shader"
+//Shader "DefaultShader"
+//{
+// SubShader
+// {
+// Pass
+// {
+// CGPROGRAM
+//
+// #pragma vertex vert
+// #pragma fragment frag
+// float4 vert (float4 pos : POSITION) : SV_POSITION { return mul(UNITY_MATRIX_MVP,pos); }
+// float4 frag () : COLOR { return float4(1,0,1,1); }
+// ENDCG
+// }
+// }
+//}
+
+
+
+static const char *gDefaultString =
+"Shader \"Default\" {"
+#if UNITY_XENON
+"SubShader { Pass { Cull Off \n"
+"Program \"vp\" { SubProgram \"xbox360 \" { Keywords { }\n"
+" Bind \"vertex\" Vertex\n"
+" Matrix 0 [glstate_matrix_mvp]\n"
+" \"vs_360\n"
+" backbbabaaaaaalmaaaaaagmaaaaaaaaaaaaaaceaaaaaaaaaaaaaajaaaaaaaaa\n"
+" aaaaaaaaaaaaaagiaaaaaabmaaaaaaflpppoadaaaaaaaaabaaaaaabmaaaaaaaa\n"
+" aaaaaafeaaaaaadaaaacaaaaaaaeaaaaaaaaaaeeaaaaaaaaghgmhdhegbhegffp\n"
+" gngbhehcgjhifpgnhghaaaklaaadaaadaaaeaaaeaaabaaaaaaaaaaaahghdfpdd\n"
+" fpdaaadccodacodjdddcdicodaaaklklaaaaaaaaaaaaaagmaaabaaabaaaaaaaa\n"
+" aaaaaaaaaaaaaaaaaaaaaaabaaaaaaabaaaaaaaaaaaaacjaaaaaaaadbaabbaad\n"
+" aaaabcaamcaaaaaaaaaaeaaeaaaabcaameaaaaaaaaaaaaadaaaaccaaaaaaaaaa\n"
+" afpibaaaaaaaagiiaaaaaaaamiapaaaaaabliiaakbabadaamiapaaaaaamgiiaa\n"
+" klabacaamiapaaaaaalbdejeklababaamiapiadoaagmaadeklabaaaaaaaaaaaa\n"
+" aaaaaaaaaaaaaaaa\"\n"
+"}}\n"
+"Program \"fp\" { SubProgram \"xbox360 \" { Keywords { }\n"
+"\"ps_360\n"
+" backbbaaaaaaaakaaaaaaageaaaaaaaaaaaaaaceaaaaaafiaaaaaaiaaaaaaaaa\n"
+" aaaaaaaaaaaaaadaaaaaaabmaaaaaacdppppadaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
+" aaaaaabmhahdfpddfpdaaadccodacodjdddcdicodaaaklklaaaaaaaaaaaaaaab\n"
+" aaaaaaaaaaaaaaaaaaaaaabeabpmaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
+" aaaaaaeaaaaaaacebaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab\n"
+" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
+" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadpiaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
+" aaaaaaaabaabmeaaccaaaaaabenmmaaaaaaaaagmmcaaaappaaaaaaaaaaaaaaaa\n"
+" aaaaaaaa\"\n"
+"}}\n"
+"}}\n"
+#elif UNITY_PS3
+"SubShader { Pass { Cull Off \n"
+"Program \"vp\" { SubProgram \"ps3 \" { Keywords { }\n"
+"Matrix 256 [glstate_matrix_mvp]\n"
+"Bind \"vertex\" Vertex\n"
+"\"sce_vp_rsx // 4 instructions using 1 registers\n"
+"[Configuration]\n"
+"8\n"
+"0000000400010100\n"
+"[Microcode]\n"
+"64\n"
+"401f9c6c01d0300d8106c0c360403f80401f9c6c01d0200d8106c0c360405f80\n"
+"401f9c6c01d0100d8106c0c360409f80401f9c6c01d0000d8106c0c360411f81\n"
+"\"\n"
+"}}\n"
+"Program \"fp\" { SubProgram \"ps3 \" { Keywords { }\n"
+"\"sce_fp_rsx // 2 instructions using 2 registers\n"
+"[Configuration]\n"
+"24\n"
+"ffffffff000000200000ffff000000000000840002000000\n"
+"[Microcode]\n"
+"32\n"
+"1e81014008021c9cc8000001c800000100003f80000000000000000000000000\n"
+"\"\n"
+"}}\n"
+"}}\n"
+
+#else
+
+#if GFX_SUPPORTS_D3D11
+"SubShader { Pass {\n"
+"Program \"vp\" {\n"
+"SubProgram \"d3d11 \" { Bind \"vertex\" Vertex ConstBuffer \"UnityPerDraw\" 336 Matrix 0 [glstate_matrix_mvp] 4 BindCB \"UnityPerDraw\" 0\n"
+"\"vs_dx11\n"
+"eefiecedijhpljdppnfhjnjaadaickkmhicpkjbcabaaaaaaheabaaaaadaaaaaa\n"
+"cmaaaaaagaaaaaaajeaaaaaaejfdeheocmaaaaaaabaaaaaaaiaaaaaacaaaaaaa\n"
+"aaaaaaaaaaaaaaaaadaaaaaaaaaaaaaaapapaaaafaepfdejfeejepeoaaklklkl\n"
+"epfdeheocmaaaaaaabaaaaaaaiaaaaaacaaaaaaaaaaaaaaaabaaaaaaadaaaaaa\n"
+"aaaaaaaaapaaaaaafdfgfpfaepfdejfeejepeoaafdeieefcniaaaaaaeaaaabaa\n"
+"dgaaaaaafjaaaaaeegiocaaaaaaaaaaaaeaaaaaafpaaaaadpcbabaaaaaaaaaaa\n"
+"ghaaaaaepccabaaaaaaaaaaaabaaaaaagiaaaaacabaaaaaadiaaaaaipcaabaaa\n"
+"aaaaaaaafgbfbaaaaaaaaaaaegiocaaaaaaaaaaaabaaaaaadcaaaaakpcaabaaa\n"
+"aaaaaaaaegiocaaaaaaaaaaaaaaaaaaaagbabaaaaaaaaaaaegaobaaaaaaaaaaa\n"
+"dcaaaaakpcaabaaaaaaaaaaaegiocaaaaaaaaaaaacaaaaaakgbkbaaaaaaaaaaa\n"
+"egaobaaaaaaaaaaadcaaaaakpccabaaaaaaaaaaaegiocaaaaaaaaaaaadaaaaaa\n"
+"pgbpbaaaaaaaaaaaegaobaaaaaaaaaaadoaaaaab\"\n"
+"}\n"
+"SubProgram \"d3d11_9x \" {\n"
+"Bind \"vertex\" Vertex\n"
+"ConstBuffer \"UnityPerDraw\" 336 \n"
+"Matrix 0 [glstate_matrix_mvp] 4\n"
+"BindCB \"UnityPerDraw\" 0\n"
+"\"vs_4_0_level_9_1\n"
+"eefieceddggiiplcpkoeljnckhkjahapknjdfpkhabaaaaaadeacaaaaaeaaaaaa\n"
+"daaaaaaaomaaaaaammabaaaaaaacaaaaebgpgodjleaaaaaaleaaaaaaaaacpopp\n"
+"iaaaaaaadeaaaaaaabaaceaaaaaadaaaaaaadaaaaaaaceaaabaadaaaaaaaaaaa\n"
+"aeaaabaaaaaaaaaaaaaaaaaaaaacpoppbpaaaaacafaaaaiaaaaaapjaafaaaaad\n"
+"aaaaapiaaaaaffjaacaaoekaaeaaaaaeaaaaapiaabaaoekaaaaaaajaaaaaoeia\n"
+"aeaaaaaeaaaaapiaadaaoekaaaaakkjaaaaaoeiaaeaaaaaeaaaaapiaaeaaoeka\n"
+"aaaappjaaaaaoeiaaeaaaaaeaaaaadmaaaaappiaaaaaoekaaaaaoeiaabaaaaac\n"
+"aaaaammaaaaaoeiappppaaaafdeieefcniaaaaaaeaaaabaadgaaaaaafjaaaaae\n"
+"egiocaaaaaaaaaaaaeaaaaaafpaaaaadpcbabaaaaaaaaaaaghaaaaaepccabaaa\n"
+"aaaaaaaaabaaaaaagiaaaaacabaaaaaadiaaaaaipcaabaaaaaaaaaaafgbfbaaa\n"
+"aaaaaaaaegiocaaaaaaaaaaaabaaaaaadcaaaaakpcaabaaaaaaaaaaaegiocaaa\n"
+"aaaaaaaaaaaaaaaaagbabaaaaaaaaaaaegaobaaaaaaaaaaadcaaaaakpcaabaaa\n"
+"aaaaaaaaegiocaaaaaaaaaaaacaaaaaakgbkbaaaaaaaaaaaegaobaaaaaaaaaaa\n"
+"dcaaaaakpccabaaaaaaaaaaaegiocaaaaaaaaaaaadaaaaaapgbpbaaaaaaaaaaa\n"
+"egaobaaaaaaaaaaadoaaaaabejfdeheocmaaaaaaabaaaaaaaiaaaaaacaaaaaaa\n"
+"aaaaaaaaaaaaaaaaadaaaaaaaaaaaaaaapapaaaafaepfdejfeejepeoaaklklkl\n"
+"epfdeheocmaaaaaaabaaaaaaaiaaaaaacaaaaaaaaaaaaaaaabaaaaaaadaaaaaa\n"
+"aaaaaaaaapaaaaaafdfgfpfaepfdejfeejepeoaa\"\n"
+"}\n"
+"}\n"
+"Program \"fp\" {\n"
+"SubProgram \"d3d11 \" {\n"
+"\"ps_dx11\n"
+"eefiecedlfbokalingbkfpbcgbelaibibjjpegjnabaaaaaalaaaaaaaadaaaaaa\n"
+"cmaaaaaadmaaaaaahaaaaaaaejfdeheoaiaaaaaaaaaaaaaaaiaaaaaaepfdeheo\n"
+"cmaaaaaaabaaaaaaaiaaaaaacaaaaaaaaaaaaaaaaaaaaaaaadaaaaaaaaaaaaaa\n"
+"apaaaaaafdfgfpfegbhcghgfheaaklklfdeieefcdiaaaaaaeaaaaaaaaoaaaaaa\n"
+"gfaaaaadpccabaaaaaaaaaaadgaaaaaipccabaaaaaaaaaaaaceaaaaaaaaaiadp\n"
+"aaaaaaaaaaaaiadpaaaaiadpdoaaaaab\"\n"
+"}\n"
+"SubProgram \"d3d11_9x \" {\n"
+"\"ps_4_0_level_9_1\n"
+"eefiecednclpehpkkhodapchnemhifoohfkahfmoabaaaaaaamabaaaaaeaaaaaa\n"
+"daaaaaaaiiaaaaaamiaaaaaaniaaaaaaebgpgodjfaaaaaaafaaaaaaaaaacpppp\n"
+"cmaaaaaaceaaaaaaaaaaceaaaaaaceaaaaaaceaaaaaaceaaaaaaceaaaaacpppp\n"
+"fbaaaaafaaaaapkaaaaaiadpaaaaaaaaaaaaiadpaaaaiadpabaaaaacaaaiapia\n"
+"aaaaoekappppaaaafdeieefcdiaaaaaaeaaaaaaaaoaaaaaagfaaaaadpccabaaa\n"
+"aaaaaaaadgaaaaaipccabaaaaaaaaaaaaceaaaaaaaaaiadpaaaaaaaaaaaaiadp\n"
+"aaaaiadpdoaaaaabejfdeheoaiaaaaaaaaaaaaaaaiaaaaaaepfdeheocmaaaaaa\n"
+"abaaaaaaaiaaaaaacaaaaaaaaaaaaaaaaaaaaaaaadaaaaaaaaaaaaaaapaaaaaa\n"
+"fdfgfpfegbhcghgfheaaklkl\"\n"
+"}\n"
+"}\n"
+"}}"
+#endif
+
+" SubShader { Tags { \"ForceSupported\" = \"True\" } Pass{ Cull Off SetTexture[_Dummy] { constantColor (1,0,1,1) combine constant }}} \n"
+#endif
+"}";
+
+int* gShaderLabContainer = NULL;
+
+static ShaderLab::IntShader* gDefaultShaderLabShader = NULL;
+static Shader* gDefaultShader = NULL;
+static Shader* s_ClearShader = NULL;
+static void MakeDefaultShaderLabShader ();
+
+Shader::Shader(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_ShaderIsBaked(false)
+{
+ MakeDefaultShaderLabShader ();
+ m_Shader = gDefaultShaderLabShader;
+ m_ShadowCasterPass = NULL;
+ m_ShadowCollectorPass = NULL;
+ m_NeedsParsing = false;
+
+ #if UNITY_EDITOR
+ m_ParsedForm = NULL;
+ #endif
+
+ if (mode != kCreateObjectFromNonMainThread)
+ PostLoad ();
+}
+
+Shader *Shader::GetDefault ()
+{
+ if (!gDefaultShader) {
+ SET_ALLOC_OWNER(gShaderLabContainer);
+ gDefaultShader = NEW_OBJECT(Shader);
+ gDefaultShader->Reset();
+ #if !UNITY_RELEASE
+ // in Shader's AwakeFromLoad it will try to parse script and call PostLoad
+ // we basically doing the same, but we assign default shader directly (w/o script)
+ gDefaultShader->HackSetAwakeWasCalled();
+ #endif
+
+ gDefaultShader->m_Shader = gDefaultShaderLabShader;
+ gDefaultShader->PostLoad();
+ gDefaultShader->SetHideFlags (kHideAndDontSave);
+ }
+ return gDefaultShader;
+}
+
+Shader* Shader::GetScreenClearShader ()
+{
+ Assert (s_ClearShader);
+ return s_ClearShader;
+}
+
+
+void Shader::LoadDefaultShaders ()
+{
+ GetDefault ();
+
+ #if GFX_SUPPORTS_D3D11 || GFX_SUPPORTS_XENON
+ if (IsGfxDevice())
+ {
+ Assert (!s_ClearShader);
+ s_ClearShader = GetBuiltinResource<Shader> ("Internal-Clear.shader");
+ }
+ #endif
+}
+
+
+Shader::~Shader ()
+{
+ if (m_Shader != gDefaultShaderLabShader)
+ UNITY_DELETE(m_Shader, kMemShader);
+
+ if (this == gDefaultShader)
+ gDefaultShader = NULL;
+
+ #if UNITY_EDITOR
+ UNITY_DELETE( m_ParsedForm, kMemShader);
+ #endif
+}
+
+bool Shader::MainThreadCleanup ()
+{
+ if (m_Shader != gDefaultShaderLabShader)
+ {
+ m_Shader->MainThreadCleanup ();
+ }
+
+ return true;
+}
+
+ShaderLab::PropertySheet *Shader::MakeProperties () const
+{
+ return m_Shader->MakeProperties ();
+}
+
+#if UNITY_EDITOR
+
+UInt32 Shader::CalculateUsedVertexComponents (bool lightmapped)
+{
+ if (m_Shader == NULL)
+ return 0;
+
+ UInt32 mask = 0;
+
+ const ShaderLab::IntShader::SubShaders& subshaders = m_Shader->GetSubShaders();
+ for (int i=0;i<subshaders.size();i++)
+ {
+ int npasses = subshaders[i]->GetTotalPassCount();
+ for (int p = 0; p < npasses; ++p)
+ {
+ mask |= subshaders[i]->GetPass(p)->CalculateUsedVertexComponents(lightmapped);
+ }
+ }
+
+ return mask;
+}
+
+int Shader::GetPropertyCount () const
+{
+ if (!m_ParsedForm)
+ return 0;
+ return m_ParsedForm->m_PropInfo.GetPropertyCount();
+}
+
+const ShaderLab::ParserProperty* Shader::GetPropertyInfo (int propertyNo) const
+{
+ if (!m_ParsedForm)
+ return NULL;
+ return m_ParsedForm->m_PropInfo.GetPropertyInfo (propertyNo);
+}
+
+bool Shader::HasClip() const
+{
+ return m_ParsedForm ? m_ParsedForm->HasClip() : false;
+}
+
+char const* Shader::GetCustomEditorName() const
+{
+ if (!m_Shader)
+ return NULL;
+ return m_Shader->GetCustomEditor ().c_str ();
+}
+
+#endif
+
+PROFILER_INFORMATION(gSetPassProfile, "Shader.SetPass", kProfilerRender);
+
+const ChannelAssigns* Shader::SetPass( int subshaderIndex, int passNo, UInt32 stateKey, const ShaderLab::PropertySheet* props )
+{
+ PROFILER_AUTO(gSetPassProfile, this)
+ return m_Shader->GetSubShader(subshaderIndex).SetPass (passNo, stateKey, props);
+}
+
+bool Shader::CanPassBeRecorded(int subshaderIndex, int passNo) const
+{
+ const ShaderLab::Pass* pass = m_Shader->GetSubShader(subshaderIndex).GetPass(passNo);
+ return pass->CanPassBeRecorded();
+}
+
+ShaderLab::Pass* Shader::GetShadowCasterPassToUse(int subshaderIndex)
+{
+ ShaderLab::SubShader& subShader = m_Shader->GetSubShader(subshaderIndex);
+ if (!subShader.ShadowCasterPassEnabled())
+ return NULL;
+ int ssPass = subShader.GetShadowCasterPassIndex();
+ if (ssPass >= 0)
+ return subShader.GetPass(ssPass);
+ return m_ShadowCasterPass;
+}
+
+ShaderLab::Pass* Shader::GetShadowCollectorPassToUse(int subshaderIndex)
+{
+ ShaderLab::SubShader& subShader = m_Shader->GetSubShader(subshaderIndex);
+ int ssPass = subShader.GetShadowCollectorPassIndex();
+ if (ssPass >= 0)
+ return subShader.GetPass(ssPass);
+ return m_ShadowCollectorPass;
+}
+
+
+int Shader::GetActiveSubShaderIndex () const
+{
+ return GetShaderLabShader()->GetActiveSubShaderIndex();
+}
+
+bool Shader::SetScript (const TextAsset::ScriptString& script)
+{
+ Super::SetScriptDontDirty (script);
+
+ m_ShadowCasterPass = NULL;
+ m_ShadowCollectorPass = NULL;
+
+ ParseAndPostLoad( script.c_str(), script.size() );
+
+ return m_Shader != gDefaultShaderLabShader;
+}
+
+
+namespace ShaderLab {
+extern const char* kPassLightModeTypeNames[kShaderPassTypeCount];
+}
+
+
+static ShaderLab::Pass* FindLightModePass( ShaderLab::IntShader* shader, ShaderPassType type )
+{
+ using ShaderLab::TagMap;
+ typedef ShaderLab::IntShader::SubShaders SubShaders;
+
+ static const int lightModeTagID = ShaderLab::GetShaderTagID("LIGHTMODE");
+
+ const SubShaders& subshaders = shader->GetSubShaders();
+ for( SubShaders::const_iterator it = subshaders.begin(); it != subshaders.end(); ++it )
+ {
+ ShaderLab::SubShader& ss = **it;
+ for( int i = 0; i < ss.GetValidPassCount (); ++i )
+ {
+ const TagMap& tags = ss.GetPass(i)->GetTags();
+ TagMap::const_iterator found = tags.find (lightModeTagID);
+ if( found != tags.end() && (StrICmp( ShaderLab::GetShaderTagName(found->second), ShaderLab::kPassLightModeTypeNames[type] ) == 0 ) )
+ {
+ return ss.GetPass(i);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void Shader::PostLoad ()
+{
+ m_Shader->PostLoad();
+
+ // Find out shadow caster/collector passes
+ Assert (m_ShadowCasterPass == NULL && m_ShadowCollectorPass == NULL);
+ m_ShadowCasterPass = FindLightModePass( m_Shader, kPassShadowCaster );
+ m_ShadowCollectorPass = FindLightModePass( m_Shader, kPassShadowCollector );
+}
+
+void Shader::AddMaterialUser( ListNode<Unity::Material>& node )
+{
+ m_Users.push_back( node );
+}
+
+PROFILER_INFORMATION(gShaderParseProfile, "Shader.Parse", kProfilerRender);
+
+void Shader::Parse (const char *str, size_t strLength)
+{
+ PROFILER_AUTO(gShaderParseProfile, this);
+ SET_ALLOC_OWNER(this);
+
+ m_NeedsParsing = false;
+ if( m_Shader != gDefaultShaderLabShader )
+ UNITY_DELETE(m_Shader, kMemShader);
+
+ #if UNITY_EDITOR
+ UNITY_DELETE( m_ParsedForm, kMemShader);
+ m_ParsedForm = NULL;
+ ShaderErrors& errors = m_Errors;
+ #else
+ ShaderErrors errors;
+ #endif
+
+ ShaderLab::ParserShader* parsedForm = NULL;
+ m_Shader = ParseShader (str, strLength, m_Dependencies, m_ShaderIsBaked, &parsedForm, errors, GetGfxDevice().GetRenderer());
+ // In editor, keep the parsed form so we can serialize shaders out specific for a platform. In players, delete parsed form now.
+ #if UNITY_EDITOR
+ m_ParsedForm = parsedForm;
+ #else
+ UNITY_DELETE( parsedForm, kMemShader);
+ #endif
+
+
+ m_ShadowCasterPass = NULL;
+ m_ShadowCollectorPass = NULL;
+ m_ShaderName = g_LastParsedShaderName;
+
+ // For no-fixed function hardware and shaders that ended up having only shadow related passes
+ // (like VertexLit), insert the pass from default shader. Otherwise the shader is supported, but
+ // has no passes suitable for any rendering path, hence all objects with it are not rendered at all.
+ if (!gGraphicsCaps.hasFixedFunction && m_Shader && gDefaultShaderLabShader)
+ {
+ ShaderLab::SubShader& ss = m_Shader->GetActiveSubShader();
+ if (ss.GetHasOnlyShadowPasses()) {
+ printf_console("Unsupported: %s\n", m_ShaderName.c_str());
+ ss.InsertDefaultPass (gDefaultShaderLabShader->GetActiveSubShader().GetPass(0));
+ }
+ }
+
+ if( !m_Shader )
+ {
+ printf_console("Unsupported: %s\n", m_ShaderName.c_str());
+ m_Shader = gDefaultShaderLabShader;
+ return;
+ }
+
+ if (m_Shader->HasNoSubShaders())
+ {
+ char buf[256];
+ snprintf(buf, 255, "No valid subshaders in '%s'.shader", GetScriptClassName().c_str());
+ ErrorStringObject (buf, this);
+ UNITY_DELETE(m_Shader, kMemShader);
+ m_Shader = gDefaultShaderLabShader;
+ }
+}
+
+
+void Shader::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ SET_ALLOC_OWNER(this);
+ Super::AwakeFromLoad (awakeMode);
+ const ScriptString& s = GetScript();
+ ParseAndPostLoad( s.c_str(), s.size() );
+}
+
+void Shader::AwakeFromLoadThreaded ()
+{
+ Super::AwakeFromLoadThreaded ();
+
+ // In some cases a shader is constructed (on the loading thread, has shader pointing
+ // to the default one, and m_NeedsParsing flag set to false). Later on
+ // actual shader is being loaded, but no one resets the m_NeedsParsing flag.
+ // This seems to happen only in a very rare combination of shaders loaded via fallbacks
+ // combined with shaders being referenced from scripts only; and even then it
+ // highly depends on the order the shaders end up being loaded. Couldn't
+ // make an automated repro case.
+ if (m_Shader == gDefaultShaderLabShader)
+ {
+ m_Shader = NULL;
+ m_ShadowCasterPass = NULL;
+ m_ShadowCollectorPass = NULL;
+ m_NeedsParsing = true;
+ }
+}
+
+template<class TransferFunc>
+void Shader::Transfer (TransferFunc& transfer)
+{
+ #if UNITY_EDITOR
+ if (transfer.IsBuildingTargetPlatform(kBuildAnyPlayerData))
+ {
+ UnityStr newSource = ProduceTargetSpecificShader (m_Script, m_ParsedForm, transfer.GetBuildingTarget());
+
+ Super::Super::Transfer (transfer);
+ transfer.Transfer (newSource, "m_Script", kHideInEditorMask);
+ transfer.Transfer (m_PathName, "m_PathName", kHideInEditorMask);
+ }
+ else
+ Super::Transfer (transfer);
+ #else
+ Super::Transfer (transfer);
+ #endif
+
+ transfer.Transfer (m_Dependencies, "m_Dependencies");
+
+ // Shaders in player data files and asset bundles should get "shader is baked" flag,
+ // so they get their dependencies directly instead of through script mapper.
+ // Need to set this flag to true when building the data files, but properly
+ // reset it back afterwards.
+ if (transfer.IsWritingGameReleaseData())
+ {
+ bool shaderIsBaked = true;
+ transfer.Transfer (shaderIsBaked, "m_ShaderIsBaked");
+ }
+ else
+ {
+ transfer.Transfer (m_ShaderIsBaked, "m_ShaderIsBaked");
+ }
+
+ transfer.Align();
+
+ #if UNITY_EDITOR
+ if (!transfer.IsSerializingForGameRelease() && !transfer.IsRemapPPtrTransfer ())
+ {
+ transfer.Transfer (m_Errors.GetErrors(), "errors");
+ transfer.Transfer (m_DefaultTextures, "m_DefaultTextures", kHideInEditorMask);
+ }
+ #endif
+
+}
+
+char const* Shader::GetName () const {
+ // shader name is the name of the shader, not the filename
+ return GetScriptClassName().c_str();
+}
+
+
+bool Shader::IsSupported () const
+{
+ return m_Shader != gDefaultShaderLabShader;
+}
+
+int Shader::GetSubShaderWithTagValue (int tagNameID, int tagValueID) const
+{
+ using ShaderLab::TagMap;
+ typedef ShaderLab::IntShader::SubShaders SubShaders;
+
+ const SubShaders& ss = m_Shader->GetSubShaders();
+ int idx = 0;
+ for( SubShaders::const_iterator i = ss.begin(); i != ss.end(); ++i )
+ {
+ const TagMap& tags = (**i).GetTags();
+ TagMap::const_iterator j = tags.find(tagNameID);
+ if (j != tags.end () && j->second == tagValueID)
+ return idx;
+ idx++;
+ }
+ return -1;
+}
+
+bool Shader::IsDependentOn (PPtr<Shader> shader) const
+{
+ for (int i = 0; i < m_Dependencies.size (); ++i)
+ if (m_Dependencies[i] == shader ||
+ m_Dependencies[i]->IsDependentOn (shader))
+ return true;
+
+ return false;
+}
+
+Shader* Shader::GetDependency (const std::string& name)
+{
+ if (!m_Shader)
+ return NULL;
+ const std::string* dependencyName = m_Shader->GetDependency (name);
+ if (!dependencyName || dependencyName->empty())
+ return NULL;
+ Shader* res = FindShaderLabShader (*dependencyName, m_Dependencies, m_ShaderIsBaked);
+ return res;
+}
+
+
+void Shader::UnloadDefaultShaderLabShader()
+{
+ UNITY_DELETE(gDefaultShaderLabShader, kMemShader);
+ gDefaultShaderLabShader = NULL;
+ if (gDefaultShader)
+ gDefaultShader->m_Shader = NULL;
+}
+
+void Shader::LoadDefaultShaderLabShader()
+{
+ ShaderLab::ParserShader* parsedForm = NULL;
+ ShaderErrors errors;
+ ShaderPtrVector dependencies;
+ gDefaultShaderLabShader = ParseShader (gDefaultString, strlen(gDefaultString), dependencies, false, &parsedForm, errors, GetGfxDevice().GetRenderer());
+ if (errors.HasErrorsOrWarnings()) {
+ ErrorString ("Default Shader has errors! Something is wrong.");
+ }
+ UNITY_DELETE(parsedForm, kMemShader);
+
+ if (gDefaultShader)
+ {
+ gDefaultShader->m_Shader = gDefaultShaderLabShader;
+ gDefaultShader->PostLoad();
+ }
+}
+
+static void MakeDefaultShaderLabShader ()
+{
+ if (gDefaultShaderLabShader)
+ return;
+ gShaderLabContainer = UNITY_NEW_AS_ROOT(int, kMemShader, "ShaderLab", "");
+ SET_ALLOC_OWNER(gShaderLabContainer);
+ ShaderLab::InitShaderLab ();
+
+ Shader::LoadDefaultShaderLabShader();
+}
+
+void CleanupShaders ()
+{
+ Shader::UnloadDefaultShaderLabShader();
+ ShaderLab::CleanupShaderLab();
+ // still some allocations left in shaderlab, so we don't want to delete this one before someone has cleaned up shaderlab
+// UNITY_DELETE(gShaderLabContainer, kMemShader);
+}
+
+void Shader::DeleteAllShaders( std::vector<SInt32>& outShaderObjects )
+{
+ outShaderObjects.clear();
+ Object::FindAllDerivedObjects (ClassID (Shader), &outShaderObjects);
+ for (std::vector<SInt32>::iterator i = outShaderObjects.begin(); i != outShaderObjects.end(); i++)
+ {
+ Shader *s = PPtr<Shader> (*i);
+ for (MaterialList::iterator sit = s->m_Users.begin(); sit != s->m_Users.end(); ++sit)
+ {
+ (*sit)->InvalidateDisplayLists();
+ }
+ if (s->m_Shader != gDefaultShaderLabShader)
+ {
+ UNITY_DELETE(s->m_Shader, kMemShader);
+ s->m_Shader = NULL;
+ s->m_ShadowCasterPass = NULL;
+ s->m_ShadowCollectorPass = NULL;
+ s->m_NeedsParsing = true;
+ }
+ }
+}
+
+void Shader::RecreateAllShaders( const std::vector<SInt32>& shaderObjects )
+{
+ for( std::vector<SInt32>::const_iterator i = shaderObjects.begin(); i != shaderObjects.end(); ++i )
+ {
+ Shader *s = PPtr<Shader> (*i);
+ if( s != gDefaultShader )
+ {
+ const ScriptString& script = s->GetScript();
+ s->ParseAndPostLoad( script.c_str(), script.size() );
+ }
+ }
+}
+
+
+void Shader::ReloadAllShaders()
+{
+ std::vector<SInt32> derivedObjects;
+ DeleteAllShaders( derivedObjects );
+ RecreateAllShaders( derivedObjects );
+}
+
+void Shader::SetGLobalMaximumShaderLOD( int lod )
+{
+ if( ShaderLab::g_GlobalMaximumShaderLOD == lod )
+ return;
+ ShaderLab::g_GlobalMaximumShaderLOD = lod;
+
+ // notify all shaders of global LOD change
+ std::vector<SInt32> shaders;
+ Object::FindAllDerivedObjects (ClassID (Shader), &shaders);
+ for (std::vector<SInt32>::iterator i = shaders.begin(); i != shaders.end(); ++i)
+ {
+ Shader* s = PPtr<Shader> (*i);
+ s->m_Shader->PostLoad();
+ }
+}
+
+int Shader::GetGlobalMaximumShaderLOD()
+{
+ return ShaderLab::g_GlobalMaximumShaderLOD;
+}
+
+void Shader::SetMaximumShaderLOD( int lod )
+{
+ if(GetMaximumShaderLOD() == lod)
+ return;
+
+ GetShaderLabShader()->SetMaximumShaderLOD(lod);
+}
+
+int Shader::GetMaximumShaderLOD( ) const
+{
+ return GetShaderLabShader()->GetMaximumShaderLOD();
+}
+
+
+IMPLEMENT_CLASS (Shader)
+IMPLEMENT_OBJECT_SERIALIZE (Shader)
diff --git a/Runtime/Shaders/Shader.h b/Runtime/Shaders/Shader.h
new file mode 100644
index 0000000..a992d1d
--- /dev/null
+++ b/Runtime/Shaders/Shader.h
@@ -0,0 +1,174 @@
+#ifndef SHADER_H
+#define SHADER_H
+
+#include "Runtime/Scripting/TextAsset.h"
+#include <string>
+#include "Runtime/Math/Color.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "External/shaderlab/Library/ShaderLabErrors.h"
+#include "Runtime/Graphics/Texture.h"
+
+using std::string;
+
+namespace ShaderLab
+{
+ class IntShader;
+ class PropertySheet;
+ struct ParserProperty;
+ class Pass;
+ struct ParserShader;
+}
+namespace Unity { class Material; }
+class Shader;
+class ChannelAssigns;
+typedef std::vector< PPtr<Shader> > ShaderPtrVector;
+
+
+// A Wrapper for a ShaderLab shader.
+// This class handles all the Unity-specific interfacing into the shader.
+class Shader : public TextAsset {
+ public:
+ REGISTER_DERIVED_CLASS (Shader, TextAsset)
+ DECLARE_OBJECT_SERIALIZE (Shader)
+
+ Shader (MemLabelId label, ObjectCreationMode mode);
+ // ~Shader (); declared-by-macro
+
+ virtual bool MainThreadCleanup ();
+
+ // Set the next pass to render
+ const ChannelAssigns* SetPass (int subshaderIndex, int passNo, UInt32 stateKey, const ShaderLab::PropertySheet* props);
+
+ bool CanPassBeRecorded(int subshaderIndex, int passNo) const;
+
+ ShaderLab::Pass* GetShadowCasterPassToUse(int subshaderIndex);
+ ShaderLab::Pass* GetShadowCollectorPassToUse(int subshaderIndex);
+
+ bool AnySubshaderHasShadowCasterPass() const { return m_ShadowCollectorPass != NULL; }
+ bool HasShadowCollectorPass() const { return m_ShadowCollectorPass != NULL; }
+ int GetActiveSubShaderIndex () const;
+
+ // Set the shader string
+ virtual bool SetScript (const ScriptString& script);
+
+ // Did this shader compile & work, or are we using the default
+ bool IsSupported () const;
+
+ // Get the default Shader
+ static Shader* GetDefault ();
+ static Shader* GetScreenClearShader ();
+ static void LoadDefaultShaders ();
+
+ static void UnloadDefaultShaderLabShader();
+ static void LoadDefaultShaderLabShader();
+ void ResetInternalPointersToNull()
+ {
+ m_Shader = NULL;
+ m_ShadowCasterPass = NULL;
+ m_ShadowCollectorPass = NULL;
+ }
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+ virtual void AwakeFromLoadThreaded ();
+
+ // Make a new property sheet.
+ ShaderLab::PropertySheet *MakeProperties () const;
+
+
+ #if UNITY_EDITOR
+ int GetPropertyCount () const;
+ const ShaderLab::ParserProperty *GetPropertyInfo (int propertyNo) const;
+ #endif
+
+ // from TextAsset, returns shader name
+ virtual const UnityStr& GetScriptClassName () const { return m_ShaderName; }
+ // from Object, returns shader name
+ virtual char const* GetName () const;
+ // GetName() is overriden, use this to return name from named object
+ // A bit of a hack, see case 364626
+ char const* GetNamedObjectName() const { return m_Name.empty() ? GetName() : m_Name.c_str (); }
+
+ const ShaderLab::IntShader *GetShaderLabShader() const { return m_Shader; }
+ ShaderLab::IntShader *GetShaderLabShader() { return m_Shader; }
+
+ // reparses and reloads all shaders
+ static void ReloadAllShaders();
+ // those two do the same as ReloadAllShaders. Use if there is something to be done in between.
+ static void DeleteAllShaders( std::vector<SInt32>& outShaderObjects );
+ static void RecreateAllShaders( const std::vector<SInt32>& shaderObjects );
+
+ static void SetGLobalMaximumShaderLOD( int lod );
+ static int GetGlobalMaximumShaderLOD();
+
+ void SetMaximumShaderLOD( int lod );
+ int GetMaximumShaderLOD() const;
+
+ // Get the index of a subshader with a given type tag.
+ int GetSubShaderWithTagValue (int tagNameID, int tagValueID) const;
+
+ void AddMaterialUser( ListNode<Unity::Material>& node );
+
+ bool GetNeedsParsing() const { return m_NeedsParsing; }
+
+ void ParseAndPostLoad( const char* str, size_t strLength ) { Parse(str,strLength); PostLoad(); }
+
+ bool IsDependentOn (PPtr<Shader> shader) const;
+ Shader* GetDependency (const std::string& name);
+ const ShaderPtrVector& GetDependencies () const { return m_Dependencies; }
+
+ ShaderLab::Pass* GetShadowCasterPass() { return m_ShadowCasterPass; }
+ ShaderLab::Pass* GetShadowCollectorPass() { return m_ShadowCollectorPass; }
+
+ #if UNITY_EDITOR
+ const ShaderLab::ParserShader* GetParsedForm() const { return m_ParsedForm; }
+ const ShaderErrors& GetErrors() const { return m_Errors; }
+ ShaderErrors& GetErrors() { return m_Errors; }
+ UInt32 CalculateUsedVertexComponents (bool lightmapped);
+
+ typedef std::map<UnityStr, PPtr<Texture> > DefaultTexturesMap;
+ const DefaultTexturesMap& GetDefaultTextures () const { return m_DefaultTextures; }
+ void SetDefaultTextures (const DefaultTexturesMap& textures) { m_DefaultTextures = textures; }
+
+ bool HasClip() const;
+ char const* GetCustomEditorName() const;
+ #endif
+
+private:
+ // Function to extract renderqueue & channels from a shader.
+ void PostLoad ();
+ // Parse a string into the shader
+ void Parse (const char *str, size_t strLength);
+
+ ShaderLab::IntShader* m_Shader; // the actual shader
+ ShaderLab::Pass* m_ShadowCasterPass; // shadow caster pass, if any
+ ShaderLab::Pass* m_ShadowCollectorPass; // shadow collector pass, if any
+
+ // List of Materials that use this shader
+ typedef List< ListNode<Unity::Material> > MaterialList;
+ MaterialList m_Users;
+
+
+ // Shaders that are not supported get assigned a "default shader". However, we really
+ // want to remember it's original name (so we can build players with proper ScriptMapper info,
+ // and display the correct name in shader popups, etc.).
+ //
+ // So remember the actual name here, and not in ShaderLab
+ // shader instance (since single "default" shaderlab instance is shared by all unsupported shaders).
+ UnityStr m_ShaderName;
+
+ ShaderPtrVector m_Dependencies; // shader pointers used by Dependencies / UsePasses / Fallbacks from this shader
+ bool m_ShaderIsBaked;
+
+ bool m_NeedsParsing;
+
+ #if UNITY_EDITOR
+ ShaderErrors m_Errors;
+ ShaderLab::ParserShader* m_ParsedForm;
+
+ DefaultTexturesMap m_DefaultTextures;
+ #endif
+};
+
+void CleanupShaders ();
+
+#endif
diff --git a/Runtime/Shaders/ShaderKeywords.cpp b/Runtime/Shaders/ShaderKeywords.cpp
new file mode 100644
index 0000000..c751574
--- /dev/null
+++ b/Runtime/Shaders/ShaderKeywords.cpp
@@ -0,0 +1,90 @@
+#include "UnityPrefix.h"
+#include "ShaderKeywords.h"
+#include <map>
+
+typedef std::map<std::string, ShaderKeyword> KeywordMap;
+
+static KeywordMap* s_KeywordMap;
+ShaderKeywordSet g_ShaderKeywords;
+
+
+void keywords::Initialize()
+{
+ Assert(!s_KeywordMap);
+ s_KeywordMap = UNITY_NEW( KeywordMap, kMemShader);
+ SET_ALLOC_OWNER(NULL);
+ // precreate common keywords
+
+ // Important: these must go first
+ Create( "SPOT" );
+ Create( "DIRECTIONAL" );
+ Create( "DIRECTIONAL_COOKIE" );
+ Create( "POINT" );
+ Create( "POINT_COOKIE" );
+
+ Create( "SHADOWS_OFF" );
+ Create( "SHADOWS_DEPTH" );
+ Create( "SHADOWS_SCREEN" );
+ Create( "SHADOWS_CUBE" );
+ Create( "SHADOWS_SOFT" );
+ Create( "SHADOWS_SPLIT_SPHERES" );
+ Create( "SHADOWS_NATIVE" );
+
+ Create( "LIGHTMAP_OFF" );
+ Create( "LIGHTMAP_ON" );
+
+ Create( "DIRLIGHTMAP_OFF" );
+ Create( "DIRLIGHTMAP_ON" );
+
+ Create( "VERTEXLIGHT_ON" );
+ Create( "SOFTPARTICLES_ON" );
+
+ Create( "HDR_LIGHT_PREPASS_ON" );
+}
+
+void keywords::Cleanup()
+{
+ UNITY_DELETE( s_KeywordMap, kMemShader);
+ s_KeywordMap = NULL;
+}
+
+ShaderKeyword keywords::Create( const std::string& name )
+{
+ if( !s_KeywordMap ) {
+ Initialize();
+ ANALYSIS_ASSUME(s_KeywordMap);
+ }
+
+ KeywordMap::const_iterator it = s_KeywordMap->find( name );
+ if( it != s_KeywordMap->end() )
+ return it->second;
+
+ ShaderKeyword key = s_KeywordMap->size();
+ if( key >= kMaxShaderKeywords )
+ {
+ AssertString( Format("Maximum number (%i) of shader keywords exceeded, keyword %s will be ignored", kMaxShaderKeywords, name.c_str()) );
+ }
+ else
+ {
+ SET_ALLOC_OWNER(NULL);
+ s_KeywordMap->insert( std::make_pair( name, key ) );
+ }
+ return key;
+}
+
+#if UNITY_EDITOR
+std::string keywords::GetKeywordName (ShaderKeyword k)
+{
+ if (!s_KeywordMap) {
+ Initialize();
+ ANALYSIS_ASSUME(s_KeywordMap);
+ }
+ for (KeywordMap::const_iterator it = s_KeywordMap->begin(); it != s_KeywordMap->end(); ++it)
+ {
+ if (it->second == k)
+ return it->first;
+ }
+ AssertString ("Requesting non existing shader keyword");
+ return "";
+}
+#endif
diff --git a/Runtime/Shaders/ShaderKeywords.h b/Runtime/Shaders/ShaderKeywords.h
new file mode 100644
index 0000000..7a273bb
--- /dev/null
+++ b/Runtime/Shaders/ShaderKeywords.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <string>
+
+typedef int ShaderKeyword;
+
+// We use about 14 builtin keywords now, 18 more for the user should be fine now
+// (shader size explosion will happen before that limit is reached anyways...)
+const int kMaxShaderKeywords = 64;
+struct ShaderKeywordSet {
+public:
+ explicit ShaderKeywordSet () : mask(0) {}
+
+ void Enable (ShaderKeyword key) { mask |= (0x1ULL<<key); }
+ void Disable (ShaderKeyword key) { mask &= ~(0x1ULL<<key); }
+ bool IsEnabled (ShaderKeyword key) const { return (mask & (0x1ULL<<key)) != 0; }
+ void Reset () { mask = 0; }
+
+ UInt64 GetMask() const { return mask; }
+ void SetMask (UInt64 m) { mask = m; }
+
+ bool operator== (const ShaderKeywordSet& o) const { return mask==o.mask; }
+ bool operator!= (const ShaderKeywordSet& o) const { return mask!=o.mask; }
+
+private:
+ UInt64 mask;
+};
+
+extern ShaderKeywordSet g_ShaderKeywords;
+
+
+namespace keywords {
+
+ void Initialize();
+ void Cleanup();
+
+ ShaderKeyword Create( const std::string& name );
+
+ #if UNITY_EDITOR
+ std::string GetKeywordName (ShaderKeyword k);
+ #endif
+
+} // namespace keywords
diff --git a/Runtime/Shaders/ShaderNameRegistry.cpp b/Runtime/Shaders/ShaderNameRegistry.cpp
new file mode 100644
index 0000000..58e9e34
--- /dev/null
+++ b/Runtime/Shaders/ShaderNameRegistry.cpp
@@ -0,0 +1,235 @@
+#include "UnityPrefix.h"
+#include "ShaderNameRegistry.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "External/shaderlab/Library/shaderlab.h"
+
+
+ScriptMapper::ScriptMapper (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_Shaders.SetObjectToDirty (this);
+}
+
+ScriptMapper::~ScriptMapper ()
+{
+}
+
+
+void ScriptMapper::AddShader (Shader& ptr)
+{
+ m_Shaders.Add (ptr.GetScriptClassName (), &ptr);
+}
+
+void ScriptMapper::AddBuiltinShader (const string& shaderClassName, PPtr<Shader> ptr)
+{
+ m_Shaders.Add (shaderClassName, ptr);
+}
+
+
+static const char* kBackwardsCompatShaderNames[] = {
+ " VertexLit", "VertexLit",
+ " Diffuse", "Diffuse",
+ " Glossy", "Specular",
+ " Bumped", "Bumped Diffuse",
+ " BumpedSpecular", "Bumped Specular",
+ "ParallaxBump/Diffuse", "Parallax Diffuse",
+ "ParallaxBump/Specular", "Parallax Specular",
+
+ "Alpha/VertexLit", "Transparent/VertexLit",
+ "Alpha/Diffuse", "Transparent/Diffuse",
+ "Alpha/Glossy", "Transparent/Specular",
+ "Alpha/Bumped", "Transparent/Bumped Diffuse",
+ "Alpha/BumpedSpecular", "Transparent/Bumped Specular",
+ "ParallaxBump/AlphaDiffuse", "Transparent/Parallax Diffuse",
+ "ParallaxBump/AlphaSpecular", "Transparent/Parallax Specular",
+
+ //"Reflective/VertexLit", "Reflective/VertexLit",
+ //"Reflective/Diffuse", "Reflective/Diffuse",
+ "Reflective/Glossy", "Reflective/Specular",
+ "Reflective/Bumped", "Reflective/Bumped Diffuse",
+ "Reflective/BumpedSpecular", "Reflective/Bumped Specular",
+ "ParallaxBump/ReflectDiffuse", "Reflective/Parallax Diffuse",
+ "ParallaxBump/ReflectSpecular", "Reflective/Parallax Specular",
+
+ "Lightmapped/Glossy", "Legacy Shaders/Lightmapped/Specular",
+ "Lightmapped/Bumped", "Legacy Shaders/Lightmapped/Bumped Diffuse",
+ "Lightmapped/BumpedSpecular", "Legacy Shaders/Lightmapped/Bumped Specular",
+ "Lightmapped/VertexLit", "Legacy Shaders/Lightmapped/VertexLit",
+ "Lightmapped/Diffuse", "Legacy Shaders/Lightmapped/Diffuse",
+ "Lightmapped/Specular", "Legacy Shaders/Lightmapped/Specular",
+ "Lightmapped/Bumped Diffuse", "Legacy Shaders/Lightmapped/Bumped Diffuse",
+ "Lightmapped/Bumped Specular", "Legacy Shaders/Lightmapped/Bumped Specular",
+ "Diffuse Fast", "Legacy Shaders/Diffuse Fast",
+
+
+ //"Self-Illumin/VertexLit", "Self-Illumin/VertexLit",
+ //"Self-Illumin/Diffuse", "Self-Illumin/Diffuse",
+ "Self-Illumin/Glossy", "Self-Illumin/Specular",
+ "Self-Illumin/Bumped", "Self-Illumin/Bumped Diffuse",
+ "Self-Illumin/BumpedSpecular", "Self-Illumin/Bumped Specular",
+ "ParallaxBump/IlluminDiffuse", "Self-Illumin/Parallax Diffuse",
+ "ParallaxBump/IlluminSpecular", "Self-Illumin/Parallax Specular",
+
+ " DiffuseDetail", "Diffuse Detail",
+ " Diffuse (fast)", "Legacy Shaders/Diffuse Fast",
+ " Decal", "Decal",
+
+ "Hidden/TerrainEngine/Splatmap/Lightmap-FirstPass", "Nature/Terrain/Diffuse",
+};
+
+
+static const char* GetNonLegacyShaderName(const std::string& name)
+{
+ const int kShaderNameCount = sizeof(kBackwardsCompatShaderNames) / sizeof(kBackwardsCompatShaderNames[0]) / 2;
+ for (int i = 0; i < kShaderNameCount; ++i)
+ {
+ if (strcmp( kBackwardsCompatShaderNames[i*2+0], name.c_str()) == 0)
+ return kBackwardsCompatShaderNames[i*2+1];
+ }
+ return NULL;
+}
+
+
+Shader *ScriptMapper::FindShader (const string &name)
+{
+ Shader* result = m_Shaders.Find (name);
+ if( result != NULL )
+ return result;
+
+ // If shader is not found, try old names (before 2.0) of built-in shaders
+ const char* otherName = GetNonLegacyShaderName(name);
+ if (otherName)
+ return m_Shaders.Find(otherName);
+
+ return NULL;
+}
+
+
+
+template<class TransferFunction>
+void ScriptMapper::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ AssertIf (transfer.GetFlags() & kPerformUnloadDependencyTracking);
+ transfer.Transfer (m_Shaders, "m_Shaders");
+}
+
+bool ScriptMapper::ShouldIgnoreInGarbageDependencyTracking ()
+{
+ return true;
+}
+IMPLEMENT_CLASS (ScriptMapper)
+IMPLEMENT_OBJECT_SERIALIZE (ScriptMapper)
+GET_MANAGER (ScriptMapper)
+GET_MANAGER_PTR(ScriptMapper)
+
+
+static void ParseShaderIfNeeded (Shader* shader)
+{
+ Assert (!shader->GetNeedsParsing() || !shader->GetShaderLabShader());
+ if (!shader->GetNeedsParsing())
+ return;
+
+ const Shader::ScriptString& script = shader->GetScript();
+ // Save current shader name around shader parsing
+ std::string saveShaderName = g_LastParsedShaderName;
+ shader->ParseAndPostLoad (script.c_str(), script.size());
+ g_LastParsedShaderName = saveShaderName;
+}
+
+
+Shader* FindShaderLabShader (const std::string& name, ShaderPtrVector& shaderLookup, bool useLookup)
+{
+ Assert(GetScriptMapperPtr());
+
+ // Finding shaders can load new ones. So save and
+ // restore the currently parsed shader name.
+ std::string saveShaderName = g_LastParsedShaderName;
+
+ Shader* shader = NULL;
+ if (useLookup)
+ {
+ for (size_t i = 0; i < shaderLookup.size(); ++i)
+ {
+ Shader* s = shaderLookup[i];
+ if (s == NULL)
+ continue;
+ ParseShaderIfNeeded (s);
+ const std::string& shaderName = s->GetScriptClassName();
+ if (shaderName == name)
+ {
+ shader = s;
+ break;
+ }
+ // also try non-legacy name of the shader we're searching for,
+ // in case someone does " Diffuse" but we're meant
+ // to find "Diffuse" etc.
+ const char* nonLegacyShaderName = GetNonLegacyShaderName(name.c_str());
+ if (nonLegacyShaderName && !strcmp(shaderName.c_str(), nonLegacyShaderName))
+ {
+ shader = s;
+ break;
+ }
+ }
+ }
+ else
+ {
+ shader = GetScriptMapper().FindShader (name);
+ }
+
+ g_LastParsedShaderName = saveShaderName;
+
+
+ if (shader)
+ {
+ ParseShaderIfNeeded (shader);
+
+ if (!useLookup)
+ {
+ PPtr<Shader> shaderPtr(shader);
+ if (std::find(shaderLookup.begin(), shaderLookup.end(), shaderPtr) == shaderLookup.end())
+ shaderLookup.push_back (shaderPtr);
+ }
+
+ return shader;
+ }
+
+ return NULL;
+}
+
+
+
+// -------------------------------------------------------------------
+
+
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/shaderlab/Library/SLParserData.h"
+
+
+static void TestCanFindLegacyShaderNamesWhenUsingLookup ()
+{
+ const char* oldName = "Lightmapped/VertexLit"; // legacy name
+ ScriptMapper& sm = GetScriptMapper();
+ Shader* shader1 = sm.FindShader(oldName);
+ Assert (shader1 != NULL);
+
+ // now check if FindShaderLabShader can find it when using lookup table
+ ShaderPtrVector lookup;
+ lookup.push_back(shader1);
+ Shader* shader2 = FindShaderLabShader (oldName, lookup, true);
+ Assert (shader2 == shader1);
+}
+
+
+void TestHighLevelShaderNameRegistryTests()
+{
+ TestCanFindLegacyShaderNamesWhenUsingLookup();
+}
+
+#endif // #if ENABLE_UNIT_TESTS
diff --git a/Runtime/Shaders/ShaderNameRegistry.h b/Runtime/Shaders/ShaderNameRegistry.h
new file mode 100644
index 0000000..1643f4d
--- /dev/null
+++ b/Runtime/Shaders/ShaderNameRegistry.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include <string>
+#include <map>
+#include "Runtime/Shaders/NameToObjectMap.h"
+#include "Runtime/Modules/ExportModules.h"
+
+using std::string;
+
+class Shader;
+typedef std::vector< PPtr<Shader> > ShaderPtrVector;
+
+
+class EXPORT_COREMODULE ScriptMapper : public GlobalGameManager
+{
+public:
+ typedef std::multimap<UnityStr, PPtr<Shader> > StringToShader;
+ REGISTER_DERIVED_CLASS (ScriptMapper, GlobalGameManager)
+ DECLARE_OBJECT_SERIALIZE (ScriptMapper)
+
+ typedef NameToObjectMap<Shader, std::map<PPtr<Shader>, UnityStr>, std::multimap<UnityStr, PPtr<Shader> > > Shaders;
+
+ ScriptMapper (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~ScriptMapper (); declared-by-macro
+
+ // Add a shader to the system.
+ void AddShader (Shader& ptr);
+ void AddBuiltinShader (const std::string& className, PPtr<Shader> ptr);
+
+ // Get a shader by name.
+ // If the shader could not be loaded (e.g. because it was deleted), it is removed from the master list.
+ Shader* FindShader (const string &name);
+
+ bool ShouldIgnoreInGarbageDependencyTracking ();
+
+ #if UNITY_EDITOR
+
+ Shader *GetDefaultShader() { return FindShader( "Diffuse" ); }
+
+ const Shaders& GetShaders () { return m_Shaders; }
+ void SetShaders (const Shaders& shaders) { m_Shaders = shaders; }
+
+ #endif // UNITY_EDITOR
+
+private:
+ Shaders m_Shaders;
+};
+
+ScriptMapper& GetScriptMapper ();
+ScriptMapper* GetScriptMapperPtr ();
+
+
+Shader* FindShaderLabShader (const std::string& name, ShaderPtrVector& shaderLookup, bool useLookup);
diff --git a/Runtime/Shaders/ShaderSupportScripts.cpp b/Runtime/Shaders/ShaderSupportScripts.cpp
new file mode 100644
index 0000000..3f60afa
--- /dev/null
+++ b/Runtime/Shaders/ShaderSupportScripts.cpp
@@ -0,0 +1,19 @@
+#include "UnityPrefix.h"
+#include "ShaderSupportScripts.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+
+IMPLEMENT_CLASS (CGProgram)
+
+CGProgram::CGProgram (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{}
+
+CGProgram::~CGProgram ()
+{
+}
+
+void CGProgram::SetCGProgram( const std::string& str )
+{
+ m_CGProgram = str;
+ m_CGProgramName = GetLastPathNameComponent(m_CGProgram);
+}
diff --git a/Runtime/Shaders/ShaderSupportScripts.h b/Runtime/Shaders/ShaderSupportScripts.h
new file mode 100644
index 0000000..e799721
--- /dev/null
+++ b/Runtime/Shaders/ShaderSupportScripts.h
@@ -0,0 +1,26 @@
+#ifndef SHADERSUPPORTSCRIPTS_H
+#define SHADERSUPPORTSCRIPTS_H
+
+#include "Runtime/Scripting/TextAsset.h"
+#include <string>
+
+
+// Shader include files (mainly exist so that a different icon can be used on them)
+class CGProgram : public TextAsset {
+public:
+ REGISTER_DERIVED_CLASS (CGProgram, TextAsset)
+
+ CGProgram (MemLabelId label, ObjectCreationMode mode);
+
+ virtual const UnityStr& GetScriptClassName() const { return m_CGProgramName; }
+
+ void SetCGProgram( const std::string& str );
+ const UnityStr& GetCGProgram () const { return m_CGProgram; }
+
+private:
+ // Placeholder string used for the original CG program - we only use this at importing
+ UnityStr m_CGProgram;
+ UnityStr m_CGProgramName;
+};
+
+#endif
diff --git a/Runtime/Shaders/ShaderTags.h b/Runtime/Shaders/ShaderTags.h
new file mode 100644
index 0000000..252b165
--- /dev/null
+++ b/Runtime/Shaders/ShaderTags.h
@@ -0,0 +1,27 @@
+#ifndef SHADER_TAGS_H
+#define SHADER_TAGS_H
+
+// The pass type of a given pass.
+// The shader sets this as a hint to the app as to how it wants to be rendered using the LightMode tag
+enum ShaderPassType {
+ kPassAlways, // Always executed. No lighting.
+ kPassVertex, // Vertex lights + ambient
+ kPassVertexLM, // Vertex lighting w/ lightmaps
+ kPassVertexLMRGBM, // Vertex lighting w/ lightmaps encoded as RGBM
+ kPassForwardBase,
+ kPassForwardAdd,
+ kPassLightPrePassBase,
+ kPassLightPrePassFinal,
+ kPassShadowCaster, // Renders object as shadow caster
+ kPassShadowCollector, // Renders object, collecting shadows into screen space texture
+ kShaderPassTypeCount // Keep this last!
+};
+
+
+enum ShaderRenderOptions {
+ kShaderOptionSoftVegetation = 0, // soft vegetation is currently on
+ kShaderRenderOptionCount // keep this last!
+};
+
+
+#endif
diff --git a/Runtime/Shaders/ShaderTests.cpp b/Runtime/Shaders/ShaderTests.cpp
new file mode 100644
index 0000000..2139543
--- /dev/null
+++ b/Runtime/Shaders/ShaderTests.cpp
@@ -0,0 +1,285 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+#include "External/UnitTest++/src/UnitTest++.h"
+
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+
+#include "External/shaderlab/Library/intshader.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/ShaderParser.h"
+#include "External/shaderlab/Library/SLParserData.h"
+#include "External/shaderlab/Library/ShaderParserInterm.h"
+#include "External/shaderlab/Library/ShaderParser.h"
+
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Utilities/Word.h"
+
+using namespace ShaderLab;
+
+static void CheckPassAffectingProps_TestHelper (const char* desc, const ShaderLab::Pass* pass, const char* propName, ...)
+{
+ const PropertyNamesSet& names = pass->GetAffectingProps();
+
+ vector_set<int> namesFound;
+ bool error = false;
+
+ // See if properties we expect do in fact affect the pass
+ const char* currName = propName;
+ va_list ap;
+ va_start (ap, propName);
+ while (currName != NULL)
+ {
+ ShaderLab::FastPropertyName currProp = ShaderLab::Property(currName);
+ if (names.names.find(currProp.index) == names.names.end())
+ {
+ printf_console("ShaderTests '%s': property %s expected to affect it, but it doesn't!\n", desc, currName);
+ error = true;
+ }
+ else
+ namesFound.insert(currProp.index);
+ currName = va_arg (ap, const char*);
+ };
+ va_end (ap);
+
+ // Now print all properties that are affecting the pass, but we didn't expect them to
+ for (vector_set<int>::const_iterator it = names.names.begin(); it != names.names.end(); ++it)
+ {
+ if (namesFound.find(*it) == namesFound.end())
+ {
+ ShaderLab::FastPropertyName prop;
+ prop.index = *it;
+ printf_console("Shader test %s: property %s is affecting shader, but is not expected to!\n", desc, prop.GetName());
+ error = true;
+ }
+ }
+
+ // If we had errors, print out all properties that are affecting the pass
+ if (error)
+ {
+ std::string msg = "..shader test failed, properties that are affecting: ";
+ for (vector_set<int>::const_iterator it = names.names.begin(); it != names.names.end(); ++it)
+ {
+ ShaderLab::FastPropertyName name;
+ name.index = *it;
+ msg += name.GetName();
+ msg += " ";
+ }
+ printf_console("%s\n\n", msg.c_str());
+ printf_console ("ShaderTests: Shader Pass affecting properties test failed, check output log");
+ CHECK (false);
+ }
+}
+
+struct ParseTestFixture
+{
+ ShaderErrors errors;
+ bool ParseTest (const char* text)
+ {
+ errors.Clear();
+ ShaderLab::ParserShader* parsedForm = PreParseShader (text, strlen(text), kGfxRendererNull, errors);
+ if (!parsedForm)
+ return false;
+ UNITY_DELETE(parsedForm,kMemShader);
+ return true;
+ }
+};
+
+SUITE (ShaderTests)
+{
+#if UNITY_EDITOR
+
+ TEST (Shader_ShadowCollectorPassCorrectZWrite_EditorOnly)
+ {
+ if (GetGfxDevice().GetRenderer() == kGfxRendererNull)
+ return;
+
+ // Ensure the presence of the 'VertexLit' shader.
+ Shader* shader = GetScriptMapper().FindShader ("VertexLit");
+ CHECK (shader != NULL);
+
+ // 'VertexLit' shader should always have shadow collector pass.
+ dynamic_array<ShaderLab::Pass*> collectorPasses;
+ shader->GetShaderLabShader()->GetActiveSubShader().FindNamedPasses ("SHADOWCOLLECTOR", collectorPasses);
+ CHECK (!collectorPasses.empty());
+
+ // shadow collector should use LEqual depth test!
+ const DeviceDepthState* depthState = collectorPasses[0]->GetState().GetDepthState();
+ CHECK (depthState);
+ CHECK (depthState->sourceState.depthFunc == kFuncLEqual);
+
+ // FindNamedPasses calls Retain(), so do not leak memory: need to release them
+ for (int i = 0; i < collectorPasses.size(); ++i)
+ collectorPasses[i]->Release();
+ }
+
+ TEST (Shader_RenderQueueRespectsLOD_EditorOnly)
+ {
+ // two subshaders with different LOD, using different render queues
+ const char* kShaderText =
+ "Shader \"__Foo\" {\n"
+ "SubShader { LOD 300 Tags {Queue=Geometry} Pass { } }\n"
+ "SubShader { LOD 100 Tags {Queue=Transparent} Pass { } }\n"
+ "}\n";
+ ShaderLab::ParserShader* parsedForm = NULL;
+ ShaderErrors errors;
+ ShaderPtrVector dependencies;
+ ShaderLab::IntShader* shader = ParseShader(kShaderText, strlen(kShaderText), dependencies, false, &parsedForm, errors, kGfxRendererD3D9);
+ CHECK (shader && !errors.HasErrorsOrWarnings());
+ CHECK (shader->GetSubShaders().size() == 2);
+
+ // uses first subshader, Geometry queue
+ CHECK (shader->GetRenderQueue() == 2000);
+
+ // set LOD to not use the first subshader
+ shader->SetMaximumShaderLOD(200);
+ // should get queue from the 2nd subshader
+ CHECK (shader->GetRenderQueue() == 3000);
+
+ UNITY_DELETE( parsedForm, kMemShader);
+ UNITY_DELETE( shader, kMemShader);
+ }
+
+ TEST (Shader_DependenciesDontHaveDuplicates_EditorOnly)
+ {
+ // two subshaders with different LOD, using different render queues
+ const char* kShaderText =
+ "Shader \"__Foo\" {\n"
+ "SubShader {\n"
+ "UsePass \"VertexLit/SHADOWCASTER\"\n"
+ "UsePass \"VertexLit/SHADOWCASTER\"\n"
+ "UsePass \"VertexLit/SHADOWCASTER\"\n"
+ "}\n"
+ "Fallback Off\n"
+ "}\n";
+ ShaderLab::ParserShader* parsedForm = NULL;
+ ShaderErrors errors;
+ ShaderPtrVector dependencies;
+ ShaderLab::IntShader* shader = ParseShader(kShaderText, strlen(kShaderText), dependencies, false, &parsedForm, errors, kGfxRendererD3D9);
+ CHECK (shader && !errors.HasErrorsOrWarnings());
+ CHECK (shader->GetSubShaders().size() == 1);
+
+ CHECK (dependencies.size() == 1);
+
+ UNITY_DELETE (parsedForm, kMemShader);
+ UNITY_DELETE (shader, kMemShader);
+ }
+
+ static void UsedVertexComponents_TestHelper (const char* name, UInt32 expected, UInt32 expectedLM)
+ {
+ Shader* shader = GetScriptMapper().FindShader (name);
+ Assert (shader);
+ UInt32 channels = shader->CalculateUsedVertexComponents(false);
+ Assert (channels == expected);
+ channels = shader->CalculateUsedVertexComponents(true);
+ Assert (channels == expectedLM);
+ }
+
+ TEST (Shader_UsedVertexComponents_EditorOnly)
+ {
+ if (gGraphicsCaps.GetEmulation() != GraphicsCaps::kEmulNone ||
+ GetGfxDevice().GetRenderer() == kGfxRendererNull ||
+ GetGfxDevice().GetRenderer() == kGfxRendererD3D11)
+ return;
+
+ UInt32 expLightingUV = (1<<kShaderChannelVertex) | (1<<kShaderChannelNormal) | (1<<kShaderChannelTexCoord0);
+ UInt32 expLightingUV2 = (1<<kShaderChannelVertex) | (1<<kShaderChannelNormal) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelTexCoord1);
+ UInt32 expLightingTanUV = (1<<kShaderChannelVertex) | (1<<kShaderChannelNormal) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelTangent);
+ UInt32 expLightingTanUV2 = (1<<kShaderChannelVertex) | (1<<kShaderChannelNormal) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelTexCoord1) | (1<<kShaderChannelTangent);
+ UInt32 expUV1 = (1<<kShaderChannelVertex) | (1<<kShaderChannelTexCoord0);
+ UInt32 expColorUV1 = (1<<kShaderChannelVertex) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelColor);
+
+ UsedVertexComponents_TestHelper ("VertexLit", expLightingUV, expLightingUV2);
+ UsedVertexComponents_TestHelper ("Diffuse", expLightingUV, expLightingUV2);
+ UsedVertexComponents_TestHelper ("Bumped Diffuse", expLightingTanUV, expLightingTanUV2);
+ UsedVertexComponents_TestHelper ("Unlit/Texture", expUV1, expUV1);
+ UsedVertexComponents_TestHelper ("Particles/Additive", expColorUV1, expColorUV1);
+ }
+
+ TEST (Shader_PassAffectingPropsWork_EditorOnly)
+ {
+ if (GetGfxDevice().GetRenderer() == kGfxRendererNull || gGraphicsCaps.shaderCaps < kShaderLevel2)
+ return;
+
+ Shader* shSpec = GetBuiltinExtraResource<Shader>("Normal-Glossy.shader");
+ CHECK (shSpec != NULL);
+
+ CheckPassAffectingProps_TestHelper("Specular ForwardBase pass", shSpec->GetShaderLabShader()->GetSubShader(0).GetPass(0), "_MainTex", "_Color", "_Shininess", "_SpecColor", "_MainTex_ST", "_ShadowMapTexture", NULL);
+ CheckPassAffectingProps_TestHelper("Specular ForwardAdd pass", shSpec->GetShaderLabShader()->GetSubShader(0).GetPass(1), "_MainTex", "_Color", "_Shininess", "_SpecColor", "_MainTex_ST", "_LightTexture0", "_LightTextureB0", NULL);
+
+ // None of user-accessible properties are affecting shadow caster pass
+ CheckPassAffectingProps_TestHelper("Specular ShadowCaster pass", shSpec->GetShadowCasterPass(), NULL);
+
+ // Specular falls back to VertexLit, so subshader #1 is vertex lit
+ CheckPassAffectingProps_TestHelper("VertexLit vertex pass", shSpec->GetShaderLabShader()->GetSubShader(1).GetPass(0), "_MainTex", "_Color", "_Shininess", "_SpecColor", "_Emission", NULL);
+
+ Shader* shCutoutBSpec = GetBuiltinExtraResource<Shader>("AlphaTest-Bumped.shader");
+ CHECK (shCutoutBSpec != NULL);
+
+ CheckPassAffectingProps_TestHelper("Cutout BumpSpec ForwardBase pass", shCutoutBSpec->GetShaderLabShader()->GetSubShader(0).GetPass(0), "_MainTex", "_Cutoff", "_BumpMap", "_Color", "_MainTex_ST", "_BumpMap_ST", "_ShadowMapTexture", NULL);
+
+ // Alpha tested shader: texture, cutoff etc. affect shadow caster pass. Bumpmap doesn't affect it.
+ CheckPassAffectingProps_TestHelper("Cutout BumpSpec pass", shCutoutBSpec->GetShadowCasterPass(), "_MainTex", "_Cutoff", "_Color", "_MainTex_ST", NULL);
+ }
+
+ TEST (Shader_MaterialGetDefaultProperties_EditorOnly)
+ {
+ // Check that a shader with some default property values actually gets them in the material.
+ const char* kShader =
+ "Shader \"\" {\n"
+ "Properties {\n"
+ "myColor (\"\", color) = (1,2,3,4)\n"
+ "myFloat (\"\", float) = 0.5\n"
+ "myRange (\"\", range(0,10)) = 5.0\n"
+ "myVector (\"\", vector) = (5,6,7,8)\n"
+ "}\n"
+ "SubShader { Pass { } }\n"
+ "}";
+ Material* mat = Material::CreateMaterial(kShader,Object::kHideAndDontSave);
+ Shader* myShader = mat->GetShader();
+
+ ShaderLab::FastPropertyName propShine = ShaderLab::Property("_Shininess");
+ ShaderLab::FastPropertyName propMyColor = ShaderLab::Property("myColor");
+ ShaderLab::FastPropertyName propMyFloat = ShaderLab::Property("myFloat");
+ ShaderLab::FastPropertyName propMyRange = ShaderLab::Property("myRange");
+ ShaderLab::FastPropertyName propMyVector = ShaderLab::Property("myVector");
+
+ // Don't compare in linear space; returned value close but slightly different due to float conversions.
+ if (GetActiveColorSpace() == kGammaColorSpace)
+ {
+ CHECK (mat->GetColor(propMyColor).Equals(ColorRGBAf(1,2,3,4)));
+ }
+ CHECK (mat->GetFloat(propMyFloat) == 0.5f);
+ CHECK (mat->GetFloat(propMyRange) == 5.0f);
+ CHECK (mat->GetColor(propMyVector).Equals(ColorRGBAf(5,6,7,8)));
+
+ // Assign a different shader.
+ Shader* vlitShader = GetScriptMapper().FindShader("VertexLit");
+ CHECK (!mat->HasProperty(propShine));
+ mat->SetShader (vlitShader);
+
+ // Now we should get _Shininess, with the default value.
+ float shininess = mat->GetFloat(propShine);
+ CHECK (shininess == 0.7f);
+
+ // Now, new shader doesn't have myFloat, so it shouldn't be in the runtime PropertySheet.
+ CHECK (!mat->HasProperty(propMyFloat));
+ // But the value should still be intact in the serialized sheet!
+ CHECK (mat->GetSavedProperties().m_Floats[propMyFloat] == 0.5f);
+
+ DestroySingleObject (mat);
+ DestroySingleObject (myShader);
+ }
+
+#endif // #if UNITY_EDITOR
+}
+
+#endif // #if ENABLE_UNIT_TESTS
diff --git a/Runtime/Shaders/UnityPropertySheet.cpp b/Runtime/Shaders/UnityPropertySheet.cpp
new file mode 100644
index 0000000..7dc894d
--- /dev/null
+++ b/Runtime/Shaders/UnityPropertySheet.cpp
@@ -0,0 +1,144 @@
+#include "UnityPrefix.h"
+#include "UnityPropertySheet.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/shaderlab/Library/SLParserData.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+
+UnityPropertySheet::UnityTexEnv::UnityTexEnv()
+{
+ m_Scale = Vector2f(1,1);
+ m_Offset = Vector2f(0,0);
+ m_Texture = 0;
+}
+
+
+#if UNITY_EDITOR
+void UnityPropertySheet::CullUnusedProperties (const ShaderLab::ParserShader* source)
+{
+ if (!source)
+ return;
+
+ TexEnvMap::iterator curTex = m_TexEnvs.begin();
+ TexEnvMap::iterator texEnd = m_TexEnvs.end();
+
+ while (curTex != texEnd)
+ {
+ const char* propName = curTex->first.GetName();
+
+ bool propFound = false;
+ for (unsigned i = 0; i < source->m_PropInfo.m_Props.size(); ++i)
+ {
+ const ShaderLab::ParserProperty& srcProp = source->m_PropInfo.m_Props[i];
+ if(srcProp.m_Type == ShaderLab::ParserProperty::kTexture
+ && ::strcmp( srcProp.m_Name.c_str(), propName ) == 0
+ )
+ {
+ propFound = true;
+ break;
+ }
+ }
+
+ if (propFound)
+ ++curTex;
+ else
+ m_TexEnvs.erase(curTex++);
+
+ }
+}
+#endif
+
+void UnityPropertySheet::AssignDefinedPropertiesTo (ShaderLab::PropertySheet &target)
+{
+ for (ShaderLab::PropertySheet::Floats::iterator i = target.GetFloatsMap().begin(); i != target.GetFloatsMap().end(); i++)
+ {
+ FloatMap::iterator j = m_Floats.find(i->first);
+ if (j != m_Floats.end())
+ {
+ i->second = j->second;
+ }
+ }
+ for (ShaderLab::PropertySheet::Vectors::iterator i = target.GetVectorMap().begin(); i != target.GetVectorMap().end(); i++)
+ {
+ ColorMap::iterator j = m_Colors.find(i->first);
+ if (j != m_Colors.end())
+ {
+ if (target.GetColorTag(i->first))
+ target.SetVector (i->first, GammaToActiveColorSpace(j->second).GetPtr());
+ else
+ target.SetVector (i->first, j->second.GetPtr());
+ }
+ }
+ for (ShaderLab::PropertySheet::TexEnvs::iterator i = target.GetTexEnvsMap().begin(); i != target.GetTexEnvsMap().end(); i++)
+ {
+ TexEnvMap::iterator j = m_TexEnvs.find(i->first);
+ if (j != m_TexEnvs.end())
+ {
+ target.SetTextureWithPlacement( i->first, j->second.m_Texture, j->second.m_Scale, j->second.m_Offset );
+ }
+ }
+}
+
+
+bool UnityPropertySheet::AddNewShaderlabProps (const ShaderLab::PropertySheet &source)
+{
+ bool addedAny = false;
+ for (ShaderLab::PropertySheet::Floats::const_iterator i = source.GetFloatsMap().begin(); i != source.GetFloatsMap().end(); i++)
+ {
+ if (m_Floats.insert( std::make_pair(i->first, i->second) ).second)
+ addedAny = true;
+ }
+ for (ShaderLab::PropertySheet::Vectors::const_iterator i = source.GetVectorMap().begin(); i != source.GetVectorMap().end(); i++)
+ {
+ // skip texture _ST & _TexelSize properties
+ bool isTextureProp = false;
+ for (ShaderLab::PropertySheet::TexEnvs::const_iterator j = source.GetTexEnvsMap().begin(); j != source.GetTexEnvsMap().end(); ++j)
+ {
+ if (j->second.scaleOffsetValue == &i->second || j->second.texelSizeValue == &i->second)
+ {
+ isTextureProp = true;
+ break;
+ }
+ }
+ if (isTextureProp)
+ continue;
+
+ if (m_Colors.insert( std::make_pair(i->first, ColorRGBAf(i->second.x, i->second.y, i->second.z, i->second.w)) ).second)
+ addedAny = true;
+ }
+ for (ShaderLab::PropertySheet::TexEnvs::const_iterator i = source.GetTexEnvsMap().begin(); i != source.GetTexEnvsMap().end(); i++)
+ {
+ if (m_TexEnvs.find (i->first) == m_TexEnvs.end())
+ {
+ UnityTexEnv ut;
+ const Matrix4x4f& mat = i->second.texEnv->GetMatrix();
+ ut.m_Scale.Set( mat.Get(0,0), mat.Get(1,1) );
+ Vector3f pos = mat.GetPosition();
+ ut.m_Offset = Vector2f (pos.x, pos.y);
+ m_TexEnvs [i->first] = ut;
+ addedAny = true;
+ }
+ }
+
+ return addedAny;
+}
+
+void UnityPropertySheet::AddNewSerializedProps (const UnityPropertySheet &source)
+{
+ for (UnityPropertySheet::FloatMap::const_iterator i = source.m_Floats.begin(); i != source.m_Floats.end(); i++)
+ {
+ if (m_Floats.find (i->first) == m_Floats.end())
+ m_Floats [i->first] = i->second;
+ }
+ for (UnityPropertySheet::ColorMap::const_iterator i = source.m_Colors.begin(); i != source.m_Colors.end(); i++)
+ {
+ if (m_Colors.find (i->first) == m_Colors.end())
+ m_Colors [i->first] = i->second;
+ }
+ for (UnityPropertySheet::TexEnvMap::const_iterator i = source.m_TexEnvs.begin(); i != source.m_TexEnvs.end(); i++)
+ {
+ if (m_TexEnvs.find (i->first) == m_TexEnvs.end())
+ m_TexEnvs [i->first] = i->second;
+ }
+}
diff --git a/Runtime/Shaders/UnityPropertySheet.h b/Runtime/Shaders/UnityPropertySheet.h
new file mode 100644
index 0000000..88d7b0d
--- /dev/null
+++ b/Runtime/Shaders/UnityPropertySheet.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include "External/shaderlab/Library/FastPropertyName.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Graphics/Texture.h"
+
+namespace ShaderLab {
+ class PropertySheet;
+ struct ParserShader;
+}
+
+// Serialized material property data (colors, textures, ...).
+// This is used only for saving & loading of materials; at runtime ShaderLab PropertySheets
+// are used (they can have more data that's not serialized, like matrices etc.).
+class UnityPropertySheet
+{
+public:
+ DECLARE_SERIALIZE (UnityPropertySheet)
+ struct UnityTexEnv {
+ UnityTexEnv();
+
+ DECLARE_SERIALIZE (UnityTexEnv)
+ Vector2f m_Scale;
+ Vector2f m_Offset;
+ PPtr<Texture> m_Texture;
+ };
+ typedef std::map<ShaderLab::FastPropertyName, UnityTexEnv> TexEnvMap;
+ typedef std::map<ShaderLab::FastPropertyName, float> FloatMap;
+ typedef std::map<ShaderLab::FastPropertyName, ColorRGBAf> ColorMap;
+ TexEnvMap m_TexEnvs;
+ FloatMap m_Floats;
+ ColorMap m_Colors;
+
+ // Set the properties of target.
+ // This attempts to fill the properties of target with any info this may have.
+ // It never adds a property to target.
+ void AssignDefinedPropertiesTo (ShaderLab::PropertySheet &target);
+
+ // Add any properties defined by source, without overwriting anything already here.
+ bool AddNewShaderlabProps (const ShaderLab::PropertySheet &source);
+ void AddNewSerializedProps (const UnityPropertySheet &source);
+
+#if UNITY_EDITOR
+ // Remove any properties not used by source.
+ // This is called when building a player by Material::Transfer
+ void CullUnusedProperties (const ShaderLab::ParserShader* source);
+#endif
+};
+
+template<class TransferFunc>
+void UnityPropertySheet::Transfer (TransferFunc& transfer)
+{
+ transfer.SetVersion (2);
+
+ TRANSFER (m_TexEnvs);
+ TRANSFER (m_Floats);
+ TRANSFER (m_Colors);
+}
+
+template<class TransferFunc>
+void UnityPropertySheet::UnityTexEnv::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (m_Texture);
+ TRANSFER (m_Scale);
+ TRANSFER (m_Offset);
+}
diff --git a/Runtime/Shaders/VBO.cpp b/Runtime/Shaders/VBO.cpp
new file mode 100644
index 0000000..700be8a
--- /dev/null
+++ b/Runtime/Shaders/VBO.cpp
@@ -0,0 +1,170 @@
+#include "UnityPrefix.h"
+#include "VBO.h"
+#include "GraphicsCaps.h"
+
+#include <cstring>
+#include "Runtime/Utilities/OptimizationUtility.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+#if UNITY_XENON
+#include <ppcintrinsics.h>
+#endif
+
+
+
+static const int kChannelVertexSize[kShaderChannelCount] = {
+ 3 * sizeof (float), // position
+ 3 * sizeof (float), // normal
+ 1 * sizeof (UInt32),// color
+ 2 * sizeof (float), // uv0
+ 2 * sizeof (float), // uv1
+ 4 * sizeof (float), // tangent
+};
+
+static const UInt8 kDefaultVertexChannelDimensionCount [kShaderChannelCount] = {
+ 3, 3, 1, 2, 2, 4
+};
+
+// -----------------------------------------------------------------------------
+// VBO common
+
+int VBO::GetDefaultChannelByteSize (int channelNum)
+{
+ DebugAssertIf( channelNum < 0 || channelNum >= kShaderChannelCount );
+ return kChannelVertexSize[channelNum];
+}
+
+int VBO::GetDefaultChannelFormat(int channelNum)
+{
+ DebugAssertIf( channelNum < 0 || channelNum >= kShaderChannelCount );
+ return (kShaderChannelColor == channelNum) ? kChannelFormatColor : kChannelFormatFloat;
+}
+
+int VBO::GetDefaultChannelDimension(int channelNum)
+{
+ return kDefaultVertexChannelDimensionCount [channelNum];
+}
+
+bool VBO::IsAnyStreamMapped() const
+{
+ for (int i = 0; i < kMaxVertexStreams; ++i)
+ if (m_IsStreamMapped[i])
+ return true;
+
+ return false;
+}
+
+bool VBO::HasStreamWithMode(StreamMode mode) const
+{
+ for (int i = 0; i < kMaxVertexStreams; ++i)
+ if (m_StreamModes[i] == mode && m_Streams[i].channelMask)
+ return true;
+
+ return false;
+}
+
+size_t GetVertexSize (unsigned shaderChannelsMask)
+{
+ size_t size = 0;
+ for (int i=0; i<kShaderChannelCount; ++i, shaderChannelsMask >>= 1)
+ if (shaderChannelsMask & 1)
+ size += kChannelVertexSize[i];
+ return size;
+}
+
+DynamicVBO::DynamicVBO()
+: m_LastChunkShaderChannelMask(0)
+, m_LastChunkStride(0)
+, m_LastChunkVertices(0)
+, m_LastChunkIndices(0)
+, m_LastRenderMode(kDrawIndexedTriangles)
+, m_LendedChunk(false)
+{
+}
+
+
+// ----------------------------------------------------------------------
+
+
+void CopyVertexStream( const VertexBufferData& sourceData, void* buffer, unsigned stream )
+{
+ DebugAssert(stream < kMaxVertexStreams);
+
+ if (!sourceData.buffer)
+ return;
+
+ const StreamInfo& info = sourceData.streams[stream];
+ const UInt8* src = static_cast<const UInt8*>(sourceData.buffer) + info.offset;
+ size_t size = CalculateVertexStreamSize(sourceData, stream);
+ #if UNITY_XENON
+ XMemCpyStreaming_WriteCombined(buffer, src, size);
+ #else
+ memcpy(buffer, src, size);
+ #endif
+}
+
+void CopyVertexBuffer( const VertexBufferData& sourceData, void* buffer )
+{
+ if (sourceData.buffer)
+ memcpy(buffer, sourceData.buffer, sourceData.bufferSize);
+}
+
+void GetVertexStreamOffsets( const VertexBufferData& sourceData, size_t dest[kShaderChannelCount], size_t baseOffset, unsigned stream )
+{
+ for (int i = 0; i < kShaderChannelCount; ++i)
+ {
+ const ChannelInfo& channel = sourceData.channels[i];
+ dest[i] = channel.IsValid() ? baseOffset + channel.CalcOffset(sourceData.streams) : 0;
+ }
+}
+
+void GetVertexStreamPointers( const VertexBufferData& sourceData, void* dest[kShaderChannelCount], void* basePtr, unsigned stream )
+{
+ for (int i = 0; i < kShaderChannelCount; ++i)
+ {
+ const ChannelInfo& channel = sourceData.channels[i];
+ dest[i] = channel.IsValid() ? (UInt8*)basePtr + channel.CalcOffset(sourceData.streams) : NULL;
+ }
+}
+
+
+void FillIndexBufferForQuads (UInt16* dst, int dstSize, const UInt16* src, int quadCount)
+{
+ int canFitQuads = dstSize / kVBOIndexSize / 6;
+ quadCount = std::min(quadCount, canFitQuads);
+
+ for (int i = 0; i < quadCount; ++i)
+ {
+ *dst++ = src[0];
+ *dst++ = src[1];
+ *dst++ = src[2];
+ *dst++ = src[0];
+ *dst++ = src[2];
+ *dst++ = src[3];
+ src += 4;
+ }
+}
+
+
+
+// --------------------------------------------------------------------------
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (VBOTests)
+{
+
+ TEST(FillIndexBufferForQuadsDoesNotOverwritePastEnd)
+ {
+ UInt16 srcBuffer[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
+ UInt16 dstBuffer[] = { 17, 13, 17, 13, 17, 13, 1337 };
+ FillIndexBufferForQuads (dstBuffer, sizeof(dstBuffer)-2, srcBuffer, 2);
+ CHECK_EQUAL (1337, dstBuffer[6]);
+ }
+
+} // SUITE
+
+#endif // ENABLE_UNIT_TESTS
diff --git a/Runtime/Shaders/VBO.h b/Runtime/Shaders/VBO.h
new file mode 100644
index 0000000..d8931ab
--- /dev/null
+++ b/Runtime/Shaders/VBO.h
@@ -0,0 +1,308 @@
+#ifndef VBO_H
+#define VBO_H
+
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Filters/Mesh/VertexData.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Modules/ExportModules.h"
+
+class ChannelAssigns;
+class Matrix4x4f;
+
+
+struct MeshPartitionInfo
+{
+ UInt32 submeshStart;
+ UInt32 partitionCount;
+ MeshPartitionInfo() {submeshStart=partitionCount=0;}
+
+ DECLARE_SERIALIZE_NO_PPTR (MeshPartitionInfo);
+};
+
+struct MeshPartition
+{
+ int vertexCount;
+ int vertexOffset;
+ int indexCount;
+ int indexByteOffset;
+ MeshPartition() { vertexCount = vertexOffset = indexCount = indexByteOffset = 0; }
+
+ DECLARE_SERIALIZE_NO_PPTR (MeshPartition);
+};
+
+struct VertexBufferData
+{
+ ChannelInfoArray channels;
+ StreamInfoArray streams;
+ UInt8* buffer;
+ int bufferSize;
+ int vertexCount;
+
+ VertexBufferData()
+ {
+ // Channels and streams have default constructors
+ buffer = 0;
+ bufferSize = 0;
+ vertexCount = 0;
+#if UNITY_PS3
+ numBones = 0;
+ numInfluences = 0;
+ inflPerVertex = 0;
+ bones = NULL;
+ influences = NULL;
+#endif
+ }
+
+#if UNITY_PS3
+ UInt32 numBones;
+ UInt32 numInfluences;
+ UInt32 inflPerVertex;
+ Matrix4x4f* bones;
+ void* influences;
+
+ UNITY_VECTOR(kMemVertexData, MeshPartitionInfo) partInfo;
+ UNITY_VECTOR(kMemVertexData, MeshPartition) partitions;
+#endif
+};
+
+struct IndexBufferData
+{
+ void* indices;
+ int count;
+ UInt32 hasTopologies; // bitmask
+};
+
+struct VertexStreamData
+{
+ UInt8* buffer;
+ UInt32 channelMask;
+ int stride;
+ int vertexCount;
+};
+
+size_t GetChannelFormatSize (UInt8 format);
+
+class EXPORT_COREMODULE VBO : public ListElement
+{
+public:
+ virtual ~VBO() { }
+
+ virtual void UpdateVertexData( const VertexBufferData& buffer ) = 0;
+ virtual void UpdateIndexData (const IndexBufferData& buffer) = 0;
+ virtual void DrawVBO (const ChannelAssigns& channels, UInt32 firstIndexByte, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 firstVertex, UInt32 vertexCount ) = 0;
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ virtual void DrawCustomIndexed( const ChannelAssigns& channels, void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount ) = 0;
+ #endif
+
+ // recreate hardware buffers
+ virtual void Recreate() { }
+
+ enum { kMaxQuads = 65536/4 - 4 }; // so we fit into 16 bit indices, minus some more just in case
+
+ // For writing directly to VBO. VBO must be filled (UpdateData)
+ // at least once; and vertex layout + topology from the last fill
+ // is used. For example, for skinned meshes you have to call
+ // UpdateData on start and each time layout/topology changes;
+ // then map,write,unmap for each skinning.
+ //
+ // In some situations a vertex buffer might become lost; then you need to do UpdateData
+ // again before using Map.
+ virtual bool MapVertexStream( VertexStreamData& outData, unsigned stream ) = 0;
+ virtual void UnmapVertexStream( unsigned stream ) = 0;
+ virtual bool IsVertexStreamMapped( unsigned stream ) const { return m_IsStreamMapped[stream]; }
+
+ virtual bool IsVertexBufferLost() const = 0;
+ virtual bool IsIndexBufferLost() const { return false; }
+
+ virtual bool IsUsingSourceVertices() const { return false; }
+ virtual bool IsUsingSourceIndices() const { return false; }
+
+ // WARNING: no checks will be done. So use wuth caution.
+ // Actually you should only use it coupled with Mesh unloading its VertexData
+ virtual void UnloadSourceVertices() {}
+
+ // Tell vertices will be mapped from render thread
+ virtual void SetMappedFromRenderThread( bool ) {}
+
+ // Whats the access pattern for modifying vertices?
+ enum StreamMode
+ {
+ kStreamModeNoAccess,
+ kStreamModeWritePersist,
+ kStreamModeDynamic,
+ kStreamModeCount
+ };
+
+ virtual void SetVertexStreamMode( unsigned stream, StreamMode mode ) { m_StreamModes[stream] = mode; }
+ StreamMode GetVertexStreamMode( unsigned stream ) const { return StreamMode(m_StreamModes[stream]); }
+ int GetStreamStride( unsigned stream = 0) const { return m_Streams[stream].stride; }
+
+ // TODO: probably unify with vertex streams mode, or extract ibo data altogether in different class
+ virtual void SetIndicesDynamic(bool dynamic) { m_IndicesDynamic = dynamic; }
+ bool AreIndicesDynamic() const { return m_IndicesDynamic; }
+
+ static int GetDefaultChannelByteSize (int channelNum);
+ static int GetDefaultChannelFormat(int channelNum);
+ static int GetDefaultChannelDimension(int channelNum);
+
+ virtual int GetRuntimeMemorySize() const = 0;
+
+ bool GetHideFromRuntimeStats() const { return m_HideFromRuntimeStats; }
+ void SetHideFromRuntimeStats( bool flag ) { m_HideFromRuntimeStats = flag; }
+
+#if GFX_SUPPORTS_D3D9
+ virtual void ResetDynamicVB() {}
+#endif
+
+ // TODO: that is actually how it would/should work in the feature
+ // or at least what i understood speaking with kaspar
+ // for now lets limit to gles2 where it is really needed
+#if GFX_SUPPORTS_OPENGLES20
+ virtual void MarkBuffersLost() {};
+#endif
+
+ virtual void UseAsStreamOutput() { }
+
+#if UNITY_XENON
+ virtual void AddExtraUvChannels( const UInt8* data, UInt32 size, int extraUvCount ) = 0;
+ virtual void CopyExtraUvChannels( VBO* source ) = 0;
+#endif
+
+protected:
+ VBO()
+ {
+ for (int i=0; i<kMaxVertexStreams; i++)
+ {
+ m_Streams[i].Reset();
+ m_StreamModes[i] = kStreamModeNoAccess;
+ m_IsStreamMapped[i] = false;
+ }
+ m_HideFromRuntimeStats = false;
+ m_IndicesDynamic = false;
+ }
+
+ bool IsAnyStreamMapped() const;
+ bool HasStreamWithMode(StreamMode mode) const;
+
+ StreamInfoArray m_Streams;
+ UInt8 m_StreamModes[kMaxVertexStreams];
+ bool m_IsStreamMapped[kMaxVertexStreams];
+ bool m_HideFromRuntimeStats;
+ bool m_IndicesDynamic;
+};
+
+
+// Usage:
+// 1) GetChunk
+// if this returns false, don't use data pointers, bail out
+// 2) fill with data
+// 3) ReleaseChunk
+// 4) DrawChunk (possibly multiple times)
+//
+// The key is that drawing must happen immediately after filling the chunk, because the next
+// GetChunk might destroy the previous chunk's data. So never count on chunks being persistent.
+class DynamicVBO {
+public:
+ virtual ~DynamicVBO() { }
+
+ enum RenderMode {
+ kDrawIndexedTriangles, // arbitrary triangle list
+ kDrawTriangleStrip, // no index buffer, one strip
+ kDrawQuads, // no index buffer, four vertices per quad
+ kDrawIndexedTriangleStrip, // arbitrary triangle strip
+ kDrawIndexedLines, // arbitraty line list
+ kDrawIndexedPoints, // arbitraty point lits
+ kDrawIndexedQuads, // arbitraty quad lits
+#if UNITY_FLASH
+ // ONLY IMPLEMENTED there for better immediate performance
+ kDrawTriangles, // no index buffer triangle list
+#endif
+ };
+
+ // Gets a chunk of vertex/index buffer to write into.
+ //
+ // maxVertices/maxIndices is the capacity of the returned chunk; you have to pass actually used
+ // amounts in ReleaseChunk afterwards.
+ //
+ // maxIndices and outIB are only used for kDrawIndexedTriangles render mode.
+ // For other ones they must be 0/NULL.
+ //
+ // Returns false if can't obtain a chunk for whatever reason.
+ virtual bool GetChunk( UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode renderMode, void** outVB, void** outIB ) = 0;
+
+ virtual void ReleaseChunk( UInt32 actualVertices, UInt32 actualIndices ) = 0;
+
+ virtual void DrawChunk (const ChannelAssigns& channels) = 0;
+
+ // dynamic vbo might be backed by real vbo that needs to be recreated
+ virtual void Recreate() {}
+
+ static bool IsIndexed(RenderMode mode) { return mode == kDrawIndexedTriangles || mode == kDrawIndexedTriangleStrip; }
+
+protected:
+ DynamicVBO();
+
+protected:
+ UInt32 m_LastChunkShaderChannelMask;
+ UInt32 m_LastChunkStride, m_LastChunkVertices, m_LastChunkIndices;
+ RenderMode m_LastRenderMode;
+ bool m_LendedChunk;
+};
+
+
+void CopyVertexStream( const VertexBufferData& sourceData, void* buffer, unsigned stream );
+void CopyVertexBuffer( const VertexBufferData& sourceData, void* buffer );
+
+void FillIndexBufferForQuads (UInt16* dst, int dstSize, const UInt16* src, int quadCount);
+
+void GetVertexStreamOffsets( const VertexBufferData& sourceData, size_t dest[kShaderChannelCount], size_t baseOffset, unsigned stream );
+void GetVertexStreamPointers( const VertexBufferData& sourceData, void* dest[kShaderChannelCount], void* basePtr, unsigned stream );
+
+static inline int GetVertexChannelSize( const VertexBufferData& buffer, int i )
+{
+ return VBO::GetDefaultChannelByteSize(i) * buffer.vertexCount;
+}
+
+int CalculateOffset( UInt32 channelMask );
+
+static inline int CalculateVertexStreamSize (const StreamInfo& stream, int vertexCount)
+{
+ return stream.stride * vertexCount;
+}
+
+static inline int CalculateVertexStreamSize (const VertexBufferData& buffer, unsigned stream)
+{
+ Assert(stream < kMaxVertexStreams);
+ return CalculateVertexStreamSize(buffer.streams[stream], buffer.vertexCount);
+}
+
+const int kVBOIndexSize = sizeof(UInt16);
+inline int CalculateIndexBufferSize (const IndexBufferData& buffer)
+{
+ int size = 0;
+ if (buffer.indices)
+ size += buffer.count * kVBOIndexSize;
+ return size;
+}
+
+static inline int GetPrimitiveCount (int indexCount, GfxPrimitiveType topology, bool nativeQuads)
+{
+ switch (topology) {
+ case kPrimitiveTriangles: return indexCount / 3;
+ case kPrimitiveTriangleStripDeprecated: return indexCount - 2;
+ case kPrimitiveQuads: return nativeQuads ? indexCount/4 : indexCount/4*2;
+ case kPrimitiveLines: return indexCount / 2;
+ case kPrimitiveLineStrip: return indexCount - 1;
+ case kPrimitivePoints: return indexCount;
+ default: Assert ("unknown primitive type"); return 0;
+ };
+}
+
+// Return vertex size in bytes, with components present as specified by the argument
+size_t GetVertexSize (unsigned shaderChannelsMask);
+
+#endif