diff options
Diffstat (limited to 'Runtime/GfxDevice/opengles30/VBOGLES30.cpp')
| -rw-r--r-- | Runtime/GfxDevice/opengles30/VBOGLES30.cpp | 1351 | 
1 files changed, 1351 insertions, 0 deletions
diff --git a/Runtime/GfxDevice/opengles30/VBOGLES30.cpp b/Runtime/GfxDevice/opengles30/VBOGLES30.cpp new file mode 100644 index 0000000..24d4bba --- /dev/null +++ b/Runtime/GfxDevice/opengles30/VBOGLES30.cpp @@ -0,0 +1,1351 @@ +#include "UnityPrefix.h" + +#if GFX_SUPPORTS_OPENGLES30 +#include "VBOGLES30.h" +#include "Runtime/GfxDevice/GfxDevice.h" +#include "Runtime/Shaders/GraphicsCaps.h" +#include "Runtime/Misc/Allocator.h" +#include "Runtime/Filters/Renderer.h" +#include "Runtime/GfxDevice/ChannelAssigns.h" +#include "Runtime/Utilities/Prefetch.h" +#include "Runtime/Math/Matrix4x4.h" +#include "Runtime/GfxDevice/BatchRendering.h" +#include "IncludesGLES30.h" +#include "AssertGLES30.h" +#include "GpuProgramsGLES30.h" +#include "DebugGLES30.h" +#include "Runtime/Profiler/TimeHelper.h" +#include "Runtime/Utilities/ArrayUtility.h" +#include "Runtime/GfxDevice/GLESChannels.h" +#include "Runtime/GfxDevice/GLDataBufferCommon.h" +#include "Runtime/Profiler/MemoryProfiler.h" + +#include <algorithm> + +#if 1 +	#define DBG_LOG_VBO_GLES30(...) {} +#else +	#define DBG_LOG_VBO_GLES30(...) {printf_console(__VA_ARGS__);printf_console("\n");} +#endif + +enum +{ +	kDefaultBufferAlign		= 64 +}; + +template <typename T> +inline T Align (T v, size_t alignment) +{ +	return (v + (alignment-1)) & ~(alignment-1); +} + +template <typename T> +inline T AlignToDefault (T v) +{ +	return Align(v, kDefaultBufferAlign); +} + +// Comparison operators for VertexArrayInfoGLES30 (used in caching) + +static inline bool operator< (const VertexInputInfoGLES30& a, const VertexInputInfoGLES30& b) +{ +	return a.componentType	< b.componentType	&& +		   a.numComponents	< b.numComponents	&& +		   a.pointer		< b.pointer			&& +		   a.stride			< b.stride; +} + +static inline bool operator!= (const VertexInputInfoGLES30& a, const VertexInputInfoGLES30& b) +{ +	return a.componentType	!= b.componentType	|| +		   a.numComponents	!= b.numComponents	|| +		   a.pointer		!= b.pointer		|| +		   a.stride			!= b.stride; +} + +static inline bool operator== (const VertexInputInfoGLES30& a, const VertexInputInfoGLES30& b) { return !(a != b); } + +static bool operator< (const VertexArrayInfoGLES30& a, const VertexArrayInfoGLES30& b) +{ +	if		(a.enabledArrays < b.enabledArrays) return true; +	else if	(b.enabledArrays < a.enabledArrays) return false; + +	// Compare buffers first as they are more likely to not match. +	for (int ndx = 0; ndx < kGLES3MaxVertexAttribs; ndx++) +	{ +		if (a.enabledArrays & (1<<ndx)) +		{ +			if		(a.buffers[ndx] < b.buffers[ndx]) return true; +			else if	(b.buffers[ndx] < a.buffers[ndx]) return false; +		} +	} + +	for (int ndx = 0; ndx < kGLES3MaxVertexAttribs; ndx++) +	{ +		if (a.enabledArrays & (1<<ndx)) +		{ +			if		(a.arrays[ndx] < b.arrays[ndx]) return true; +			else if	(b.arrays[ndx] < a.arrays[ndx]) return false; +		} +	} + +	return false; // Equal +} + +static bool operator!= (const VertexArrayInfoGLES30& a, const VertexArrayInfoGLES30& b) +{ +	if (a.enabledArrays != b.enabledArrays) +		return true; + +	// Compare buffers first as they are more likely to not match. +	for (int ndx = 0; ndx < kGLES3MaxVertexAttribs; ndx++) +	{ +		if (a.enabledArrays & (1<<ndx)) +		{ +			if (a.buffers[ndx] != b.buffers[ndx]) +				return true; +		} +	} + +	for (int ndx = 0; ndx < kGLES3MaxVertexAttribs; ndx++) +	{ +		if (a.enabledArrays & (1<<ndx)) +		{ +			if (a.arrays[ndx] != b.arrays[ndx]) +				return true; +		} +	} + +	return false; +} + +static bool operator== (const VertexArrayInfoGLES30& a, const VertexArrayInfoGLES30& b) { return !(a != b); } + +struct CompareVertexArrayInfoGLES30 +{ +	bool operator() (const VertexArrayInfoGLES30* a, const VertexArrayInfoGLES30* b) const; +}; + +class VertexArrayObjectGLES30 +{ +public: +									VertexArrayObjectGLES30		(const VertexArrayInfoGLES30& info); +									~VertexArrayObjectGLES30	(void); + +	UInt32							GetVAO						(void) const { return m_vao;	} +	const VertexArrayInfoGLES30*	GetInfo						(void) const { return &m_info;	} + +private: +									VertexArrayObjectGLES30		(const VertexArrayObjectGLES30& other); // Not allowed! +	VertexArrayObjectGLES30&		operator=					(const VertexArrayObjectGLES30& other); // Not allowed! + +	VertexArrayInfoGLES30			m_info; +	UInt32							m_vao; +}; + +typedef std::map<const VertexArrayInfoGLES30*, VertexArrayObjectGLES30*, CompareVertexArrayInfoGLES30> VertexArrayMapGLES30; + +// Utilities + +static const GLenum kTopologyGLES3[] = +{ +	GL_TRIANGLES, +	GL_TRIANGLE_STRIP, +	GL_TRIANGLES, +	GL_LINES, +	GL_LINE_STRIP, +	GL_POINTS, +}; +typedef char kTopologyGLES3SizeAssert[ARRAY_SIZE(kTopologyGLES3) == kPrimitiveTypeCount ? 1 : -1]; + +static const GLenum kVertexTypeGLES3[] = +{ +	GL_FLOAT,			// kChannelFormatFloat +	GL_HALF_FLOAT,		// kChannelFormatFloat16 +	GL_UNSIGNED_BYTE,	// kChannelFormatColor +	GL_BYTE,			// kChannelFormatByte +}; +typedef char kVertexTypeGLES3Assert[ARRAY_SIZE(kVertexTypeGLES3) == kChannelFormatCount ? 1 : -1]; + +// Targets by attribute location +static const VertexComponent kVertexCompTargetsGLES3[] = +{ +	// \note Indices must match to attribute locations. +	kVertexCompVertex, +	kVertexCompColor, +	kVertexCompNormal, +	kVertexCompTexCoord0, +	kVertexCompTexCoord1, +	kVertexCompTexCoord2, +	kVertexCompTexCoord3, +	kVertexCompTexCoord4, +	kVertexCompTexCoord5, +	kVertexCompTexCoord6, +	kVertexCompTexCoord7 +}; +typedef char kVertexCompTargetsGLES3Assert[ARRAY_SIZE(kVertexCompTargetsGLES3) == kGLES3MaxVertexAttribs ? 1 : -1]; + +// For some reason GfxDevice needs to know whether VBO contains color +// data in order to set up state properly. So this must be called before +// doing BeforeDrawCall(). +void VBOContainsColorGLES30 (bool flag); // defined in GfxDeviceGLES30.cpp + +static bool IsVertexDataValid (const VertexBufferData& buffer) +{ +	// Verify streams. +	{ +		UInt32 shaderChannels = 0; +		for (int streamNdx = 0; streamNdx < kMaxVertexStreams; streamNdx++) +		{ +			if ((buffer.streams[streamNdx].channelMask == 0) != (buffer.streams[streamNdx].stride == 0)) +				return false; // No data but enabled channels, or other way around. + +			if ((shaderChannels & buffer.streams[streamNdx].channelMask) != 0) +				return false; // Duplicate channels! + +			shaderChannels |= buffer.streams[streamNdx].channelMask; +		} +	} + +	// Make sure channels point to correct streams. +	for (int chanNdx = 0; chanNdx < kShaderChannelCount; chanNdx++) +	{ +		if (buffer.channels[chanNdx].dimension == ChannelInfo::kInvalidDimension) +			continue; + +		int streamNdx = buffer.channels[chanNdx].stream; + +		if (streamNdx < 0 || streamNdx >= kMaxVertexStreams) +			return false; + +		if (buffer.streams[streamNdx].channelMask & (1<<chanNdx) == 0) +			return false; // No such channel in stream. +	} + +	// Verify that streams do not overlap (or otherwise we waste memory). +	// \todo [pyry] O(n^2), but then again kMaxVertexStreams = 4.. +	for (int streamNdx = 0; streamNdx < kMaxVertexStreams; streamNdx++) +	{ +		if (buffer.streams[streamNdx].channelMask == 0) +			continue; + +		const int	start		= buffer.streams[streamNdx].offset; +		const int	end			= start + buffer.streams[streamNdx].stride*buffer.vertexCount; + +		for (int otherStreamNdx = 0; otherStreamNdx < kMaxVertexStreams; otherStreamNdx++) +		{ +			if (otherStreamNdx == streamNdx || +				buffer.streams[otherStreamNdx].channelMask == 0) +				continue; + +			const int	otherStart	= buffer.streams[otherStreamNdx].offset; +			const int	otherEnd	= otherStart + buffer.streams[otherStreamNdx].stride*buffer.vertexCount; + +			if ((start <= otherStart && otherStart < end) || // Start lies inside buffer +				(start < otherEnd && otherEnd <= end)) // End lies inside buffer +				return false; +		} +	} + +	return true; +} + +static bool IsVertexArrayNormalized (int attribNdx, VertexChannelFormat format) +{ +	if (attribNdx == 0) +		return false; // Position is never normalized. +	else +		return (format != kChannelFormatFloat && format != kChannelFormatFloat16); +} + +// VAOCacheGLES30 + +VAOCacheGLES30::VAOCacheGLES30 (void) +	: m_NextEntryNdx(0) +{ +} + +VAOCacheGLES30::~VAOCacheGLES30 (void) +{ +	Clear(); +} + +void VAOCacheGLES30::Clear (void) +{ +	for (int ndx = 0; ndx < kCacheSize; ndx++) +	{ +		delete m_Entries[ndx].vao; +		m_Entries[ndx].vao	= 0; +		m_Entries[ndx].key	= VAOCacheKeyGLES30(); +	} + +	m_NextEntryNdx = 0; // Not necessary, but this will place first VAOs in first slots. +} + +inline const VertexArrayObjectGLES30* VAOCacheGLES30::Find (const VAOCacheKeyGLES30& key) const +{ +	for (int ndx = 0; ndx < kCacheSize; ndx++) +	{ +		if (m_Entries[ndx].key == key) +			return m_Entries[ndx].vao; +	} + +	return 0; +} + +void VAOCacheGLES30::Insert (const VAOCacheKeyGLES30& key, VertexArrayObjectGLES30* vao) +{ +	Assert(!Find(key)); + +	// Replace last +	delete m_Entries[m_NextEntryNdx].vao; +	m_Entries[m_NextEntryNdx].key = key; +	m_Entries[m_NextEntryNdx].vao = vao; +	m_NextEntryNdx = (m_NextEntryNdx + 1) % kCacheSize; +} + +bool VAOCacheGLES30::IsFull (void) const +{ +	return m_Entries[m_NextEntryNdx].vao != 0; +} + +// VertexArrayObjectGLES30 + +VertexArrayObjectGLES30::VertexArrayObjectGLES30 (const VertexArrayInfoGLES30& info) +	: m_info(info) +	, m_vao	(0) +{ +	UInt32 boundBuffer = 0; + +	GLES_CHK(glGenVertexArrays(1, (GLuint*)&m_vao)); +	GLES_CHK(glBindVertexArray(m_vao)); + +	for (int attribNdx = 0; attribNdx < kGLES3MaxVertexAttribs; attribNdx++) +	{ +		if ((info.enabledArrays & (1<<attribNdx)) == 0) +			continue; + +		const UInt32	buffer				= info.buffers[attribNdx]; +		const int		numComponents		= info.arrays[attribNdx].numComponents; +		const GLenum	compType			= kVertexTypeGLES3[info.arrays[attribNdx].componentType]; +		const bool		normalized			= IsVertexArrayNormalized(attribNdx, (VertexChannelFormat)info.arrays[attribNdx].componentType); +		const int		stride				= info.arrays[attribNdx].stride; +		const void*		pointer				= info.arrays[attribNdx].pointer; + +		if (buffer != boundBuffer) +		{ +			GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, buffer)); +			boundBuffer = buffer; +		} + +		GLES_CHK(glEnableVertexAttribArray(attribNdx)); +		GLES_CHK(glVertexAttribPointer(attribNdx, numComponents, compType, normalized ? GL_TRUE : GL_FALSE, stride, pointer)); +	} + +	GLES_CHK(glBindVertexArray(0)); +	GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, 0)); +} + +VertexArrayObjectGLES30::~VertexArrayObjectGLES30 (void) +{ +	glDeleteVertexArrays(1, (const GLuint*)&m_vao); +} + +// GLES3VBO + +GLES3VBO::GLES3VBO (void) +	: m_IndexBuffer(0) +{ +} + +GLES3VBO::~GLES3VBO (void) +{ +	Cleanup(); +} + +static UInt32 MapStreamModeToBufferUsage (VBO::StreamMode mode) +{ +	switch (mode) +	{ +		case VBO::kStreamModeNoAccess:		return GL_STATIC_DRAW; // ??? +		case VBO::kStreamModeWritePersist:	return GL_STATIC_DRAW; +		case VBO::kStreamModeDynamic:		return GL_STREAM_DRAW; +		default: +			return GL_STATIC_DRAW; +	} +} + +inline DataBufferGLES30* GLES3VBO::GetCurrentBuffer (int streamNdx) +{ +	return m_StreamBuffers[streamNdx].buffers[m_StreamBuffers[streamNdx].curBufferNdx]; +} + +void GLES3VBO::UpdateVertexData (const VertexBufferData& buffer) +{ +	Assert(IsVertexDataValid(buffer)); + +	bool	streamHasData[kMaxVertexStreams]; +	int		streamBufSize[kMaxVertexStreams]; + +	for (int streamNdx = 0; streamNdx < kMaxVertexStreams; streamNdx++) +	{ +		const bool	hasData		= (buffer.streams[streamNdx].channelMask != 0); +		const int	size		= buffer.streams[streamNdx].stride * buffer.vertexCount; + +		Assert(hasData == (size != 0)); + +		streamHasData[streamNdx]	= hasData; +		streamBufSize[streamNdx]	= size; +	} + +	// Discard all existing buffers. +	for (int streamNdx = 0; streamNdx < kMaxVertexStreams; streamNdx++) +	{ +		Stream& stream = m_StreamBuffers[streamNdx]; + +		for (int bufNdx = 0; bufNdx < kBufferSwapChainSize; bufNdx++) +		{ +			if (stream.buffers[bufNdx]) +			{ +				stream.buffers[bufNdx]->Release(); +				stream.buffers[bufNdx] = 0; +			} +		} + +		UNITY_FREE(kMemVertexData, stream.cpuBuf); +		stream.cpuBuf = 0; +	} + +	m_VAOCache.Clear(); // VAO cache must be cleared + +	// Allocate buffers and upload data. +	for (int streamNdx = 0; streamNdx < kMaxVertexStreams; streamNdx++) +	{ +		if (!streamHasData[streamNdx]) +			continue; + +		const UInt32	usage		= MapStreamModeToBufferUsage((StreamMode)m_StreamModes[streamNdx]); +		const int		size		= streamBufSize[streamNdx]; +		const UInt8*	streamData	= buffer.buffer + buffer.streams[streamNdx].offset; +		Stream&			dstStream	= m_StreamBuffers[streamNdx]; + +		Assert(!dstStream.buffers[dstStream.curBufferNdx]); + +		dstStream.buffers[dstStream.curBufferNdx] = GetBufferManagerGLES30()->AcquireBuffer(size, usage); +		dstStream.buffers[dstStream.curBufferNdx]->RecreateWithData(size, usage, streamData); + +		dstStream.channelMask	= buffer.streams[streamNdx].channelMask; +		dstStream.stride		= buffer.streams[streamNdx].stride; + +		// \todo [2013-06-19 pyry] Allocate cpuBuf on-demand once it is not required for Recreate() +		dstStream.cpuBuf		= (UInt8*)UNITY_MALLOC(kMemVertexData, size); +		::memcpy(dstStream.cpuBuf, streamData, size); +	} + +	m_VertexCount = buffer.vertexCount; +	memcpy(&m_Channels[0], &buffer.channels[0], sizeof(ChannelInfoArray)); +} + +void GLES3VBO::UpdateIndexData (const IndexBufferData& buffer) +{ +	const int		bufferSize		= CalculateIndexBufferSize(buffer); +	const UInt32	bufferUsage		= m_IndicesDynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW; + +	Assert(bufferSize > 0); + +	if (m_IndexBuffer && BufferUpdateCausesStallGLES30(m_IndexBuffer)) +	{ +		m_IndexBuffer->Release(); +		m_IndexBuffer = 0; +	} + +	if (!m_IndexBuffer) +		m_IndexBuffer = GetBufferManagerGLES30()->AcquireBuffer(bufferSize, bufferUsage); + +	m_IndexBuffer->RecreateWithData(bufferSize, bufferUsage, buffer.indices); + +	// Take copy for emulating quads. +	Assert(kVBOIndexSize == sizeof(UInt16)); +	m_Indices.resize(buffer.count); +	std::copy((const UInt16*)buffer.indices, (const UInt16*)buffer.indices + buffer.count, m_Indices.begin()); +} + +bool GLES3VBO::MapVertexStream (VertexStreamData& outData, unsigned stream) +{ +	Assert(0 <= stream && stream < kMaxVertexStreams); +	Assert(!m_IsStreamMapped[stream]); // \note Multiple mappings will screw up buffer swap chain. +	Assert(m_StreamBuffers[stream].buffers[m_StreamBuffers[stream].curBufferNdx]); + +	Stream&		mapStream	= m_StreamBuffers[stream]; +	const int	size		= m_VertexCount * mapStream.stride; +	void*		mapPtr		= 0; + +	if (BufferUpdateCausesStallGLES30(mapStream.buffers[mapStream.curBufferNdx])) +	{ +		// Advance to next slot. +		mapStream.curBufferNdx = (mapStream.curBufferNdx + 1) % kBufferSwapChainSize; + +		if (!mapStream.buffers[mapStream.curBufferNdx]) +		{ +			const UInt32		usage		= MapStreamModeToBufferUsage((StreamMode)m_StreamModes[stream]); +			DataBufferGLES30*	mapBuffer	= GetBufferManagerGLES30()->AcquireBuffer(size, usage); + +			if (mapBuffer->GetSize() < size) +				mapBuffer->RecreateStorage(size, usage); + +			mapStream.buffers[mapStream.curBufferNdx] = mapBuffer; +		} +	} + +	if (gGraphicsCaps.gles30.useMapBuffer) +	{ +		// \note Using write-only mapping always as map is not guaranteed to return old contents anyway. +		mapPtr = mapStream.buffers[mapStream.curBufferNdx]->Map(0, size, GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_BUFFER_BIT); +	} +	else +	{ +		// \todo [2013-06-19 pyry] Allocate cpuBuf on-demand once it is not required for Recreate() +		Assert(mapStream.cpuBuf); +		mapPtr = mapStream.cpuBuf; +	} + +	outData.channelMask	= mapStream.channelMask; +	outData.stride		= mapStream.stride; +	outData.vertexCount	= m_VertexCount; +	outData.buffer		= (UInt8*)mapPtr; + +	m_IsStreamMapped[stream] = true; + +	return true; +} + +void GLES3VBO::UnmapVertexStream (unsigned stream) +{ +	Assert(0 <= stream && stream < kMaxVertexStreams); +	Assert(m_IsStreamMapped[stream]); + +	if (gGraphicsCaps.gles30.useMapBuffer) +	{ +		GetCurrentBuffer(stream)->Unmap(); +	} +	else +	{ +		const int		size	= m_StreamBuffers[stream].stride*m_VertexCount; +		const UInt32	usage	= MapStreamModeToBufferUsage((StreamMode)m_StreamModes[stream]); + +		// \note Buffer slot was advanced in MapVertexStream() +		GetCurrentBuffer(stream)->RecreateWithData(size, usage, m_StreamBuffers[stream].cpuBuf); +	} + +	m_IsStreamMapped[stream] = false; +} + +void GLES3VBO::Cleanup (void) +{ +	if (m_IndexBuffer) +	{ +		m_IndexBuffer->Release(); +		m_IndexBuffer = 0; +	} + +	for (int streamNdx = 0; streamNdx < kMaxVertexStreams; streamNdx++) +	{ +		for (int bufNdx = 0; bufNdx < kBufferSwapChainSize; bufNdx++) +		{ +			if (m_StreamBuffers[streamNdx].buffers[bufNdx]) +			{ +				m_StreamBuffers[streamNdx].buffers[bufNdx]->Release(); +				m_StreamBuffers[streamNdx].buffers[bufNdx] = 0; +			} +		} + +		UNITY_FREE(kMemVertexData, m_StreamBuffers[streamNdx].cpuBuf); +		m_StreamBuffers[streamNdx].cpuBuf = 0; +	} + +	m_VAOCache.Clear(); +} + +void GLES3VBO::Recreate (void) +{ +	// \note Called on context loss. + +	if (m_IndexBuffer) +	{ +		const int		bufferSize	= (int)m_Indices.size() * kVBOIndexSize; +		const UInt32	bufferUsage	= m_IndicesDynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW; + +		m_IndexBuffer->Disown(); +		delete m_IndexBuffer; + +		m_IndexBuffer = GetBufferManagerGLES30()->AcquireBuffer(bufferSize, bufferUsage); +		m_IndexBuffer->RecreateWithData(bufferSize, bufferUsage, &m_Indices[0]); +	} + +	for (int streamNdx = 0; streamNdx < kMaxVertexStreams; streamNdx++) +	{ +		Stream&			stream		= m_StreamBuffers[streamNdx]; +		const int		bufSize		= stream.stride*m_VertexCount; +		const UInt32	usage		= MapStreamModeToBufferUsage((StreamMode)m_StreamModes[streamNdx]); + +		for (int bufNdx = 0; bufNdx < kBufferSwapChainSize; bufNdx++) +		{ +			if (stream.buffers[bufNdx]) +			{ +				stream.buffers[bufNdx]->Disown(); +				delete stream.buffers[bufNdx]; +				stream.buffers[bufNdx] = 0; +			} +		} + +		stream.curBufferNdx = 0; + +		if (bufSize > 0) +		{ +			Assert(stream.cpuBuf); +			stream.buffers[0] = GetBufferManagerGLES30()->AcquireBuffer(bufSize, usage); +			stream.buffers[0]->RecreateWithData(bufSize, usage, stream.cpuBuf); +		} +	} + +	m_VAOCache.Clear(); +} + +bool GLES3VBO::IsVertexBufferLost (void) const +{ +	return false; +} + +bool GLES3VBO::IsUsingSourceVertices (void) const +{ +	return false; +} + +bool GLES3VBO::IsUsingSourceIndices (void) const +{ +	return true; +} + +int GLES3VBO::GetRuntimeMemorySize (void) const +{ +	int totalSize = 0; + +	for (int streamNdx = 0; streamNdx < kMaxVertexStreams; streamNdx++) +	{ +		for (int bufNdx = 0; bufNdx < kBufferSwapChainSize; bufNdx++) +		{ +			if (m_StreamBuffers[streamNdx].buffers[bufNdx]) +				totalSize += m_StreamBuffers[streamNdx].buffers[bufNdx]->GetSize(); +		} +	} + +	if (m_IndexBuffer) +		totalSize += m_IndexBuffer->GetSize(); + +	return totalSize; +} + +void GLES3VBO::ComputeVertexInputState (VertexArrayInfoGLES30& dst, const ChannelAssigns& channelAssigns) +{ +	UInt32	availableShaderChannels		= 0; // Channels that have data in any of streams. +	UInt32	enabledTargets				= channelAssigns.GetTargetMap(); + +	for (int streamNdx = 0; streamNdx < kMaxVertexStreams; streamNdx++) +		availableShaderChannels |= m_StreamBuffers[streamNdx].channelMask; + +	for (int attribNdx = 0; attribNdx < kGLES3MaxVertexAttribs; attribNdx++) +	{ +		const VertexComponent target = kVertexCompTargetsGLES3[attribNdx]; + +		if ((enabledTargets & (1<<target)) == 0) +			continue; // Not enabled. + +		const ShaderChannel	sourceChannel = channelAssigns.GetSourceForTarget(target); + +		// \todo [pyry] Uh, what? Channel is enabled, but no valid source exists. +		if (sourceChannel < 0) +			continue; + +		Assert(0 <= sourceChannel && sourceChannel < kShaderChannelCount); + +		if ((availableShaderChannels & (1<<sourceChannel)) == 0) +			continue; // Not available. + +		const Stream& stream = m_StreamBuffers[m_Channels[sourceChannel].stream]; + +		dst.arrays[attribNdx].componentType		= m_Channels[sourceChannel].format; +		dst.arrays[attribNdx].numComponents		= m_Channels[sourceChannel].format == kChannelFormatColor ? 4 : m_Channels[sourceChannel].dimension; +		dst.arrays[attribNdx].pointer			= reinterpret_cast<const void*>(m_Channels[sourceChannel].offset); +		dst.arrays[attribNdx].stride			= stream.stride; +		dst.buffers[attribNdx]					= GetCurrentBuffer(m_Channels[sourceChannel].stream)->GetBuffer(); + +		dst.enabledArrays |= (1<<attribNdx); +	} + +	// Fixed-function texgen stuff. +	{ +		GfxDevice&	device			= GetRealGfxDevice(); +		const int	texArrayBase	= kGLES3AttribLocationTexCoord0; +		const int	maxTexArrays	= std::min(gGraphicsCaps.maxTexUnits, kGLES3MaxVertexAttribs-texArrayBase); +		 +		if (device.IsPositionRequiredForTexGen() && (availableShaderChannels & (1 << kShaderChannelVertex))) +		{ +			const ChannelInfo&	posInfo		= m_Channels[kShaderChannelVertex]; +			const Stream&		posStream	= m_StreamBuffers[posInfo.stream]; + +			for (int texUnit = 0; texUnit < maxTexArrays; ++texUnit) +			{ +				if (device.IsPositionRequiredForTexGen(texUnit)) +				{ +					const int arrNdx = texArrayBase + texUnit; +					dst.arrays[arrNdx].componentType		= posInfo.format; +					dst.arrays[arrNdx].numComponents		= posInfo.format == kChannelFormatColor ? 4 : posInfo.dimension; +					dst.arrays[arrNdx].pointer				= reinterpret_cast<const void*>(posInfo.offset); +					dst.arrays[arrNdx].stride				= posStream.stride; +					dst.buffers[arrNdx]						= GetCurrentBuffer(posInfo.stream)->GetBuffer(); + +					dst.enabledArrays |= (1<<arrNdx); +				} +			} +		} + +		if (device.IsNormalRequiredForTexGen() && (availableShaderChannels & (1 << kShaderChannelNormal))) +		{ +			const ChannelInfo&	normInfo	= m_Channels[kShaderChannelNormal]; +			const Stream&		normStream	= m_StreamBuffers[normInfo.stream]; + +			for (int texUnit = 0; texUnit < maxTexArrays; ++texUnit) +			{ +				if (device.IsNormalRequiredForTexGen(texUnit)) +				{ +					const int arrNdx = texArrayBase + texUnit; +					dst.arrays[arrNdx].componentType		= normInfo.format; +					dst.arrays[arrNdx].numComponents		= normInfo.format == kChannelFormatColor ? 4 : normInfo.dimension; +					dst.arrays[arrNdx].pointer				= reinterpret_cast<const void*>(normInfo.offset); +					dst.arrays[arrNdx].stride				= normStream.stride; +					dst.buffers[arrNdx]						= GetCurrentBuffer(normInfo.stream)->GetBuffer(); + +					dst.enabledArrays |= (1<<arrNdx); +				} +			} +		} +	} +} + +void GLES3VBO::MarkBuffersRendered (const ChannelAssigns& channelAssigns) +{ +	// \todo [pyry] Mark based on channel assignments +	for (int ndx = 0; ndx < kMaxVertexStreams; ndx++) +	{ +		DataBufferGLES30* buffer = GetCurrentBuffer(ndx); +		if (buffer) +			buffer->RecordRender(); +	} +} + +void GLES3VBO::DrawVBO (const ChannelAssigns&	channels, +						UInt32					firstIndexByte, +						UInt32					indexCount, +						GfxPrimitiveType		topology, +						UInt32					firstVertex, +						UInt32					vertexCount) +{ +	Assert(0 <= firstVertex && firstVertex+vertexCount <= m_VertexCount); + +	if (topology == kPrimitiveQuads) +	{ +		// Need to emulate - oh well. +		// \todo [2013-05-24 pyry] Cache this? +		const int			numQuads			= indexCount/4; +		const int			emulatedIndexCount	= numQuads * 6; +		const int			emulatedBufSize		= emulatedIndexCount * sizeof(UInt16); +		const UInt32		bufUsage			= GL_DYNAMIC_DRAW; +		std::vector<UInt16>	quadIndices			(emulatedIndexCount); +		DataBufferGLES30*	indexBuffer			= GetBufferManagerGLES30()->AcquireBuffer(emulatedBufSize, bufUsage); + +		FillIndexBufferForQuads(&quadIndices[0], emulatedBufSize, (const UInt16*)((UInt8*)&m_Indices[0] + firstIndexByte), numQuads); +		indexBuffer->RecreateWithData(emulatedBufSize, bufUsage, &quadIndices[0]); + +		Draw(indexBuffer, channels, kPrimitiveTriangles, emulatedIndexCount, 0, vertexCount); + +		indexBuffer->Release(); +	} +	else +		Draw(m_IndexBuffer, channels, topology, indexCount, firstIndexByte, vertexCount); +} + +void GLES3VBO::DrawCustomIndexed (const ChannelAssigns&	channels, +								  void*					indices, +								  UInt32				indexCount, +								  GfxPrimitiveType		topology, +								  UInt32				vertexRangeBegin, +								  UInt32				vertexRangeEnd, +								  UInt32				drawVertexCount) +{ +	Assert(0 <= vertexRangeBegin && vertexRangeBegin <= vertexRangeEnd && vertexRangeEnd <= m_VertexCount); + +	// \note Called only in static batching mode which doesn't do quads. +	Assert(topology != kPrimitiveQuads); + +	const int			ndxBufSize		= indexCount * kVBOIndexSize; +	const UInt32		ndxBufUsage		= GL_DYNAMIC_DRAW; +	DataBufferGLES30*	indexBuffer		= GetBufferManagerGLES30()->AcquireBuffer(ndxBufSize, ndxBufUsage); + +	indexBuffer->RecreateWithData(ndxBufSize, ndxBufUsage, indices); + +	Draw(indexBuffer, channels, topology, indexCount, 0, vertexRangeEnd-vertexRangeBegin); + +	indexBuffer->Release(); +} + +void GLES3VBO::Draw (DataBufferGLES30*		indexBuffer, +					 const ChannelAssigns&	channels, +					 GfxPrimitiveType		topology, +					 UInt32					indexCount, +					 UInt32					indexOffset, +					 UInt32					vertexCountForStats) +{ +	const bool						useVAO	= true; // \todo [pyry] Get from GfxDevice caps +	const VertexArrayObjectGLES30*	vao		= useVAO ? TryGetVAO(channels) : 0; + +	Assert(topology != kPrimitiveQuads); + +	// Setup other render state +	VBOContainsColorGLES30(channels.GetSourceForTarget (kVertexCompColor) == kShaderChannelColor); +	GetRealGfxDevice().BeforeDrawCall(false); + +	if (vao) +	{ +		GLES_CHK(glBindVertexArray(vao->GetVAO())); +	} +	else +	{ +		VertexArrayInfoGLES30 vertexState; +		ComputeVertexInputState(vertexState, channels); +		SetupDefaultVertexArrayStateGLES30(vertexState); +	} + +	GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer->GetBuffer())); + +	{ +		ABSOLUTE_TIME	drawTime	= START_TIME; +		const int		primCount	= GetPrimitiveCount(indexCount, topology, false); + +		GLES_CHK(glDrawElements(kTopologyGLES3[topology], indexCount, GL_UNSIGNED_SHORT, (const void*)indexOffset)); + +		drawTime = ELAPSED_TIME(drawTime); +		GetRealGfxDevice().GetFrameStats().AddDrawCall(primCount, vertexCountForStats, drawTime); +	} + +	GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + +	if (vao) +	{ +		GLES_CHK(glBindVertexArray(0)); +	} + +	// Record render event for used buffers +	MarkBuffersRendered(channels); +	indexBuffer->RecordRender(); +} + +const VertexArrayObjectGLES30* GLES3VBO::TryGetVAO (const ChannelAssigns& channels) +{ +	// \note [pyry] VAO cache keys don't handle texgen. It is a rare case and we don't want to pay the +	//				cost of extra logic & storage. +	{ +		GfxDevice& device = GetRealGfxDevice(); +		if (device.IsPositionRequiredForTexGen() || device.IsNormalRequiredForTexGen()) +			return 0; +	} + +	const VAOCacheKeyGLES30 cacheKey(channels, m_StreamBuffers[0].curBufferNdx, +											   m_StreamBuffers[1].curBufferNdx, +											   m_StreamBuffers[2].curBufferNdx, +											   m_StreamBuffers[3].curBufferNdx); + +	const VertexArrayObjectGLES30* cachedVAO = m_VAOCache.Find(cacheKey); + +	if (cachedVAO) +	{ +		return cachedVAO; +	} +	else if (!m_VAOCache.IsFull()) +	{ +		DBG_LOG_VBO_GLES30("GLES3VBO::GetVAO(): cache miss, creating new VAO"); + +		// Map channel assigns + current layout to full vertex state +		VertexArrayInfoGLES30 vertexState; +		ComputeVertexInputState(vertexState, channels); + +		VertexArrayObjectGLES30* vao = new VertexArrayObjectGLES30(vertexState); +		m_VAOCache.Insert(cacheKey, vao); + +		return vao; +	} +	else +	{ +		// If VAO cache gets full, VBO falls back to using default VAO. That +		// way we avoid constantly creating new objects in extreme cases. +		return 0; +	} +} + +UInt32 GLES3VBO::GetSkinningTargetVBO (void) +{ +	const int	skinStreamNdx	= 0; // \todo [2013-05-31 pyry] Can this change? +	Stream&		skinStream		= m_StreamBuffers[skinStreamNdx]; + +	Assert(skinStream.buffers[skinStream.curBufferNdx]); + +	if (BufferUpdateCausesStallGLES30(skinStream.buffers[skinStream.curBufferNdx])) +	{ +		// Move to next slot. +		skinStream.curBufferNdx = (skinStream.curBufferNdx + 1) % kBufferSwapChainSize; + +		if (!skinStream.buffers[skinStream.curBufferNdx]) +		{ +			const UInt32		usage	= GL_STREAM_DRAW; +			const int			size	= m_VertexCount*skinStream.stride; +			DataBufferGLES30*	buffer	= GetBufferManagerGLES30()->AcquireBuffer(size, usage); + +			if (buffer->GetSize() < size) +				buffer->RecreateStorage(size, usage); + +			skinStream.buffers[skinStream.curBufferNdx] = buffer; +		} +	} + +	skinStream.buffers[skinStream.curBufferNdx]->RecordUpdate(); +	return skinStream.buffers[skinStream.curBufferNdx]->GetBuffer(); +} + +DynamicGLES3VBO::DynamicGLES3VBO (void) +	: m_CurVertexBuffer			(0) +	, m_CurIndexBuffer			(0) +	, m_CurRenderMode			((RenderMode)0) +	, m_CurShaderChannelMask	(0) +	, m_CurStride				(0) +	, m_CurVertexCount			(0) +	, m_CurIndexCount			(0) +	, m_QuadArrayIndexBuffer	(0) +{ +} + +DynamicGLES3VBO::~DynamicGLES3VBO (void) +{ +	Cleanup(); +} + +static UInt32 GetDynamicChunkStride (UInt32 shaderChannelMask) +{ +	UInt32 stride = 0; +	for (int i = 0; i < kShaderChannelCount; ++i) +	{ +		if (shaderChannelMask & (1<<i)) +			stride += VBO::GetDefaultChannelByteSize(i); +	} +	return stride; +} + +bool DynamicGLES3VBO::GetChunk (UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode renderMode, void** outVB, void** outIB) +{ +	Assert(maxVertices < 65536 && maxIndices < 65536*3); +	Assert(!((renderMode == kDrawQuads) && (VBO::kMaxQuads*4 < maxVertices))); +	DebugAssert(outVB != NULL && maxVertices > 0); +	DebugAssert((renderMode == kDrawIndexedQuads			&& (outIB != NULL && maxIndices > 0)) || +				(renderMode == kDrawIndexedPoints			&& (outIB != NULL && maxIndices > 0)) || +				(renderMode == kDrawIndexedLines			&& (outIB != NULL && maxIndices > 0)) || +				(renderMode == kDrawIndexedTriangles		&& (outIB != NULL && maxIndices > 0)) || +				(renderMode == kDrawIndexedTriangleStrip	&& (outIB != NULL && maxIndices > 0)) || +				(renderMode == kDrawTriangleStrip			&& (outIB == NULL && maxIndices == 0)) || +				(renderMode == kDrawQuads					&& (outIB == NULL && maxIndices == 0))); +	DebugAssert(!m_CurVertexBuffer && !m_CurIndexBuffer); + +	const UInt32		stride				= GetDynamicChunkStride(shaderChannelMask); +	const UInt32		usage				= GL_STREAM_DRAW; +	const UInt32		vertexBufferSize	= AlignToDefault(stride*maxVertices); +	const UInt32		indexBufferSize		= AlignToDefault(kVBOIndexSize*maxIndices); + +	const bool			useMapBuffer		= gGraphicsCaps.gles30.useMapBuffer; +	const bool			mapVertexBuffer		= useMapBuffer && vertexBufferSize >= kDataBufferThreshold; +	const bool			mapIndexBuffer		= useMapBuffer && indexBufferSize > 0 && indexBufferSize >= kDataBufferThreshold; + +	DataBufferGLES30*	vertexBuffer		= mapVertexBuffer ? GetBufferManagerGLES30()->AcquireBuffer(maxVertices*stride, usage) : 0; +	DataBufferGLES30*	indexBuffer			= mapIndexBuffer ? GetBufferManagerGLES30()->AcquireBuffer(indexBufferSize, usage) : 0; + +	// \todo [2013-05-31 pyry] Grow buffers in reasonable steps (align to 1k?) +	if (vertexBuffer && vertexBuffer->GetSize() < vertexBufferSize) +		vertexBuffer->RecreateStorage(vertexBufferSize, usage); + +	if (indexBuffer && indexBuffer->GetSize() < indexBufferSize) +		indexBuffer->RecreateStorage(indexBufferSize, usage); + +	if (!mapVertexBuffer && m_CurVertexData.size() < vertexBufferSize) +		m_CurVertexData.resize(vertexBufferSize); + +	if (!mapIndexBuffer && m_CurIndexData.size() < indexBufferSize) +		m_CurIndexData.resize(indexBufferSize); + +	if (vertexBuffer) +		*outVB = vertexBuffer->Map(0, vertexBufferSize, GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_BUFFER_BIT|GL_MAP_FLUSH_EXPLICIT_BIT); +	else if (!mapVertexBuffer && vertexBufferSize > 0) +		*outVB = &m_CurVertexData[0]; + +	if (indexBuffer) +		*outIB = indexBuffer->Map(0, indexBufferSize, GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_BUFFER_BIT|GL_MAP_FLUSH_EXPLICIT_BIT); +	else if (!mapIndexBuffer && indexBufferSize > 0) +		*outIB = &m_CurIndexData[0]; + +	m_CurVertexBuffer		= vertexBuffer; +	m_CurIndexBuffer		= indexBuffer; +	m_CurRenderMode			= renderMode; +	m_CurShaderChannelMask	= shaderChannelMask; +	m_CurStride				= stride; + +	return true; +} + +void DynamicGLES3VBO::ReleaseChunk (UInt32 actualVertices, UInt32 actualIndices) +{ +	Assert(m_CurVertexCount == 0 && m_CurIndexCount == 0); + +	if (m_CurVertexBuffer) +	{ +		m_CurVertexBuffer->FlushMappedRange(0, AlignToDefault(m_CurStride*actualVertices)); +		m_CurVertexBuffer->Unmap(); +	} +	else if (actualVertices*m_CurStride >= kDataBufferThreshold) +	{ +		// Migrate to buffer. +		const int		size	= AlignToDefault(actualVertices*m_CurStride); +		const UInt32	usage	= GL_STREAM_DRAW; + +		m_CurVertexBuffer = GetBufferManagerGLES30()->AcquireBuffer(size, usage); +		m_CurVertexBuffer->RecreateWithData(size, usage, &m_CurVertexData[0]); +	} + +	if (m_CurIndexBuffer) +	{ +		m_CurIndexBuffer->FlushMappedRange(0, AlignToDefault(actualIndices*kVBOIndexSize)); +		m_CurIndexBuffer->Unmap(); +	} +	else if (actualIndices*kVBOIndexSize >= kDataBufferThreshold) +	{ +		// Migrate to buffer. +		const int		size	= AlignToDefault(actualIndices*kVBOIndexSize); +		const UInt32	usage	= GL_STREAM_DRAW; + +		m_CurIndexBuffer = GetBufferManagerGLES30()->AcquireBuffer(size, usage); +		m_CurIndexBuffer->RecreateWithData(size, usage, &m_CurIndexData[0]); +	} + +	m_CurVertexCount	= actualVertices; +	m_CurIndexCount		= actualIndices; +} + +DataBufferGLES30* DynamicGLES3VBO::GetQuadArrayIndexBuffer (int vertexCount) +{ +	const int		quadCount			= vertexCount/4; +	const int		quadIndexCount		= quadCount * 6; +	const int		indexBufferSize		= quadIndexCount * sizeof(UInt16); +	const UInt32	indexBufferUsage	= GL_STATIC_DRAW; + +	if (!m_QuadArrayIndexBuffer || m_QuadArrayIndexBuffer->GetSize() < indexBufferSize) +	{ +		// Need to re-specify index buffer since current is too small +		std::vector<UInt16> quadIndices(quadIndexCount); + +		for (int quadNdx = 0; quadNdx < quadCount; ++quadNdx) +		{ +			const UInt16	srcBaseNdx	= quadNdx*4; +			const int		dstBaseNdx	= quadNdx*6; +			quadIndices[dstBaseNdx + 0] = srcBaseNdx + 1; +			quadIndices[dstBaseNdx + 1] = srcBaseNdx + 2; +			quadIndices[dstBaseNdx + 2] = srcBaseNdx; +			quadIndices[dstBaseNdx + 3] = srcBaseNdx + 2; +			quadIndices[dstBaseNdx + 4] = srcBaseNdx + 3; +			quadIndices[dstBaseNdx + 5] = srcBaseNdx; +		} + +		if (m_QuadArrayIndexBuffer && BufferUpdateCausesStallGLES30(m_QuadArrayIndexBuffer)) +		{ +			m_QuadArrayIndexBuffer->Release(); +			m_QuadArrayIndexBuffer = 0; +		} + +		if (!m_QuadArrayIndexBuffer) +			m_QuadArrayIndexBuffer = GetBufferManagerGLES30()->AcquireBuffer(indexBufferSize, indexBufferUsage); + +		m_QuadArrayIndexBuffer->RecreateWithData(indexBufferSize, indexBufferUsage, &quadIndices[0]); +	} + +	return m_QuadArrayIndexBuffer; +} + +void DynamicGLES3VBO::DrawChunk (const ChannelAssigns& channels) +{ +	// Compute input state +	VertexArrayInfoGLES30 vertexInputState; +	ComputeVertexInputState(vertexInputState, channels); + +	// \todo [2013-05-31 pyry] Do we want to use VAOs here? + +	// Setup state +	VBOContainsColorGLES30(channels.GetSourceForTarget (kVertexCompColor) == kShaderChannelColor); +	GetRealGfxDevice().BeforeDrawCall(false); +	SetupDefaultVertexArrayStateGLES30(vertexInputState); + +	const void*	indexPtr			= m_CurIndexBuffer ? 0 : (m_CurIndexData.empty() ? 0 : &m_CurIndexData[0]); +	int			trianglesForStats	= 0; + +	if (m_CurIndexBuffer) +		GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_CurIndexBuffer->GetBuffer())); + +	switch (m_CurRenderMode) +	{ +		case kDrawIndexedQuads: // \todo [2013-06-13 pyry] This enum shouldn't even be here. +		case kDrawIndexedTriangles: +			GLES_CHK(glDrawElements(GL_TRIANGLES, m_CurIndexCount, GL_UNSIGNED_SHORT, indexPtr)); +			trianglesForStats = m_CurIndexCount/3; +			break; + +		case kDrawIndexedTriangleStrip: +			GLES_CHK(glDrawElements(GL_TRIANGLE_STRIP, m_CurIndexCount, GL_UNSIGNED_SHORT, indexPtr)); +			trianglesForStats = std::max<int>(0, m_CurIndexCount-2); +			break; +		case kDrawIndexedPoints: +			GLES_CHK(glDrawElements(GL_POINTS, m_CurIndexCount, GL_UNSIGNED_SHORT, indexPtr)); +			trianglesForStats = m_CurIndexCount*2; // Assuming one quad +			break; + +		case kDrawIndexedLines: +			GLES_CHK(glDrawElements(GL_LINES, m_CurIndexCount, GL_UNSIGNED_SHORT, indexPtr)); +			trianglesForStats = m_CurIndexCount; +			break; + +		case kDrawTriangleStrip: +			GLES_CHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, m_CurVertexCount)); +			trianglesForStats = m_CurVertexCount-2; +			break; + +		case kDrawQuads: +		{ +			// Need to emulate with indices. +			DataBufferGLES30* quadIndexBuf = GetQuadArrayIndexBuffer(m_CurVertexCount); +			GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadIndexBuf->GetBuffer())); +			GLES_CHK(glDrawElements(GL_TRIANGLES, m_CurVertexCount/4 * 6, GL_UNSIGNED_SHORT, 0)); +			GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +			trianglesForStats = m_CurVertexCount/2; +			quadIndexBuf->RecordRender(); +			break; +		} + +		default: +			Assert(false); +	} + +	if (m_CurIndexBuffer) +	{ +		GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +		m_CurIndexBuffer->RecordRender(); +	} + +	GetRealGfxDevice().GetFrameStats().AddDrawCall(trianglesForStats, m_CurVertexCount); + +	// Release buffers and reset state +	Cleanup(); +} + +void DynamicGLES3VBO::Recreate (void) +{ +	Cleanup(); +} + +void DynamicGLES3VBO::Cleanup (void) +{ +	if (m_CurVertexBuffer) +	{ +		m_CurVertexBuffer->Release(); +		m_CurVertexBuffer = 0; +	} + +	if (m_CurIndexBuffer) +	{ +		m_CurIndexBuffer->Release(); +		m_CurIndexBuffer = 0; +	} + +	if (m_QuadArrayIndexBuffer) +	{ +		m_QuadArrayIndexBuffer->Release(); +		m_QuadArrayIndexBuffer = 0; +	} + +	m_CurRenderMode			= (RenderMode)0; +	m_CurShaderChannelMask	= 0; +	m_CurStride				= 0; +	m_CurVertexCount		= 0; +	m_CurIndexCount			= 0; + +	m_CurVertexData.clear(); +	m_CurIndexData.clear(); +} + +void DynamicGLES3VBO::ComputeVertexInputState (VertexArrayInfoGLES30& dst, const ChannelAssigns& channelAssigns) +{ +	const UInt32	availableShaderChannels		= m_CurShaderChannelMask; // Channels that have data in any of streams. +	const UInt32	enabledTargets				= channelAssigns.GetTargetMap(); +	UInt32			channelOffsets[kShaderChannelCount]; + +	const UInt8*	basePointer					= m_CurVertexBuffer ? 0 : &m_CurVertexData[0]; +	const UInt32	buffer						= m_CurVertexBuffer ? m_CurVertexBuffer->GetBuffer() : 0; + +	// Compute offsets per enabled shader channel. +	{ +		UInt32 curOffset = 0; +		for (int ndx = 0; ndx < kShaderChannelCount; ndx++) +		{ +			if (availableShaderChannels & (1<<ndx)) +			{ +				channelOffsets[ndx] = curOffset; +				curOffset += VBO::GetDefaultChannelByteSize(ndx); +			} +			else +				channelOffsets[ndx] = 0; +		} +	} + +	for (int attribNdx = 0; attribNdx < kGLES3MaxVertexAttribs; attribNdx++) +	{ +		const VertexComponent target = kVertexCompTargetsGLES3[attribNdx]; + +		if ((enabledTargets & (1<<target)) == 0) +			continue; // Not enabled. + +		const ShaderChannel	sourceChannel = channelAssigns.GetSourceForTarget(target); + +		// \todo [pyry] Uh, what? Channel is enabled, but no valid source exists. +		if (sourceChannel < 0) +			continue; + +		Assert(0 <= sourceChannel && sourceChannel < kShaderChannelCount); + +		if ((availableShaderChannels & (1<<sourceChannel)) == 0) +			continue; // Not available. + +		dst.arrays[attribNdx].componentType		= VBO::GetDefaultChannelFormat(sourceChannel); +		dst.arrays[attribNdx].numComponents		= dst.arrays[attribNdx].componentType == kChannelFormatColor ? 4 : VBO::GetDefaultChannelDimension(sourceChannel); +		dst.arrays[attribNdx].pointer			= basePointer + channelOffsets[sourceChannel]; +		dst.arrays[attribNdx].stride			= m_CurStride; +		dst.buffers[attribNdx]					= buffer; + +		dst.enabledArrays |= (1<<attribNdx); +	} + +	// Fixed-function texgen stuff. \todo [pyry] Are these even used? +	{ +		GfxDevice&	device			= GetRealGfxDevice(); +		const int	texArrayBase	= 3; +		const int	maxTexArrays	= std::min(gGraphicsCaps.maxTexUnits, kGLES3MaxVertexAttribs-texArrayBase); +		 +		if (device.IsPositionRequiredForTexGen() && (availableShaderChannels & (1 << kShaderChannelVertex))) +		{ +			const ShaderChannel	srcChannel	= kShaderChannelVertex; +			const int			format		= VBO::GetDefaultChannelFormat(srcChannel); +			const int			numComps	= VBO::GetDefaultChannelDimension(srcChannel); +			const UInt32		offset		= channelOffsets[srcChannel]; + +			for (int texUnit = 0; texUnit < maxTexArrays; ++texUnit) +			{ +				if (device.IsPositionRequiredForTexGen(texUnit)) +				{ +					const int arrNdx = texArrayBase + texUnit; +					dst.arrays[arrNdx].componentType		= format; +					dst.arrays[arrNdx].numComponents		= numComps; +					dst.arrays[arrNdx].pointer				= basePointer + offset; +					dst.arrays[arrNdx].stride				= m_CurStride; +					dst.buffers[arrNdx]						= buffer; + +					dst.enabledArrays |= (1<<arrNdx); +				} +			} +		} + +		if (device.IsNormalRequiredForTexGen() && (availableShaderChannels & (1 << kShaderChannelNormal))) +		{ +			const ShaderChannel	srcChannel	= kShaderChannelNormal; +			const int			format		= VBO::GetDefaultChannelFormat(srcChannel); +			const int			numComps	= VBO::GetDefaultChannelDimension(srcChannel); +			const UInt32		offset		= channelOffsets[srcChannel]; + +			for (int texUnit = 0; texUnit < maxTexArrays; ++texUnit) +			{ +				if (device.IsNormalRequiredForTexGen(texUnit)) +				{ +					const int arrNdx = texArrayBase + texUnit; +					dst.arrays[arrNdx].componentType		= format; +					dst.arrays[arrNdx].numComponents		= numComps; +					dst.arrays[arrNdx].pointer				= basePointer + offset; +					dst.arrays[arrNdx].stride				= m_CurStride; +					dst.buffers[arrNdx]						= buffer; + +					dst.enabledArrays |= (1<<arrNdx); +				} +			} +		} +	} +} + +// \todo [2013-05-31 pyry] Better, more generic state cache + +// \note In theory we could use whole VertexArrayInfoGLES30 as state cache, but alas +//		 bound buffers can be destroyed, recreated and state cache would be oblivious +//		 to the fact that binding is now empty. +static UInt32 sEnabledArrays = 0; + +void InvalidateVertexInputCacheGLES30() +{ +	sEnabledArrays = 0; + +	for (int attribNdx = 0; attribNdx < gGraphicsCaps.gles30.maxAttributes; attribNdx++) +		GLES_CHK(glDisableVertexAttribArray(attribNdx)); +} + +void SetupDefaultVertexArrayStateGLES30 (const VertexArrayInfoGLES30& info) +{ +	UInt32 curBoundBuffer = 0; +	GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, 0)); + +	for (int attribNdx = 0; attribNdx < kGLES3MaxVertexAttribs; attribNdx++) +	{ +		const UInt32 enableBit = 1<<attribNdx; + +		if (info.enabledArrays & enableBit) +		{ +			if (!(sEnabledArrays & enableBit)) +				GLES_CHK(glEnableVertexAttribArray(attribNdx)); + +			const UInt32	buffer				= info.buffers[attribNdx]; +			const int		numComponents		= info.arrays[attribNdx].numComponents; +			const GLenum	compType			= kVertexTypeGLES3[info.arrays[attribNdx].componentType]; +			const bool		normalized			= IsVertexArrayNormalized(attribNdx, (VertexChannelFormat)info.arrays[attribNdx].componentType); +			const int		stride				= info.arrays[attribNdx].stride; +			const void*		pointer				= info.arrays[attribNdx].pointer; + +			if (curBoundBuffer != buffer) +			{ +				GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, buffer)); +				curBoundBuffer = buffer; +			} + +			GLES_CHK(glVertexAttribPointer(attribNdx, numComponents, compType, normalized ? GL_TRUE : GL_FALSE, stride, pointer)); +		} +		else if (sEnabledArrays & enableBit) +			GLES_CHK(glDisableVertexAttribArray(attribNdx)); +	} + +	sEnabledArrays = info.enabledArrays; +} + +#endif // GFX_SUPPORTS_OPENGLES30  | 
