1#include "ContextGLES30.h"
3#include "BuffersGLES30.h"
4#include "TexturesGLES30.h"
5#include "EffectsGLES30.h"
6#include "RenderTargetsGLES30.h"
7#include "FormatsGLES30.h"
8#include "CapabilitiesGLES30.h"
11#include <webgl/webgl2.h>
22 GL_ONE_MINUS_SRC_COLOR,
24 GL_ONE_MINUS_SRC_ALPHA,
26 GL_ONE_MINUS_DST_ALPHA,
28 GL_ONE_MINUS_DST_COLOR,
29 GL_SRC_ALPHA_SATURATE,
31 GL_ONE_MINUS_CONSTANT_COLOR
33 static_assert(
sizeof(Blends) ==
sizeof(Blends[0]) * size_t(Cogs::BlendState::Blend::Count));
35 GLenum BlendOperations[] = {
38 GL_FUNC_REVERSE_SUBTRACT,
42 static_assert(
sizeof(BlendOperations) ==
sizeof(BlendOperations[0]) * size_t(Cogs::BlendState::BlendOperation::Count));
44 GLenum DepthFunctions[] = {
55 GLenum glBufferTargets[] = {
57 GL_ELEMENT_ARRAY_BUFFER,
62 GL_TRANSFORM_FEEDBACK_BUFFER
64 static_assert(
sizeof(glBufferTargets) ==
sizeof(glBufferTargets[0]) * size_t(Cogs::OpenGLES30::BufferTarget::Count));
68 switch (texture.target) {
70 if (texture.textureId == 0) {
71 LOG_ERROR_ONCE(logger,
"Cannot resolve to/from null texture");
76 if (texture.renderBuffer == 0) {
77 LOG_ERROR_ONCE(logger,
"Cannot resolve to/from null renderbuffer");
82 LOG_ERROR_ONCE(logger,
"Cannot resolve to/from this kind of texture target");
86 glGenFramebuffers(1, &texture.fbo);
87 glBindFramebuffer(framebufferTarget, texture.fbo);
91 switch (texture.target) {
93 glFramebufferTexture2D(framebufferTarget, framebufferAttachment, GL_TEXTURE_2D, texture.textureId, 0);
96 glFramebufferRenderbuffer(framebufferTarget, framebufferAttachment, GL_RENDERBUFFER, texture.renderBuffer);
103 GLenum status = glCheckFramebufferStatus(framebufferTarget);
104 if (status != GL_FRAMEBUFFER_COMPLETE) {
105 LOG_ERROR_ONCE(logger,
"Failed to set up framebuffer: %s", Cogs::OpenGLES30::framebuffersStatusString(status));
106 glBindFramebuffer(GL_FRAMEBUFFER, 0);
116Cogs::IEffects* Cogs::ContextGLES30::getEffects() {
return effects; }
120 syncObjects->signal(fenceHandle);
123void Cogs::ContextGLES30::setupVertexAttributes(
size_t baseVertex)
125 if (inputLayout.currHandle && currentEffect.ptr) {
130 uint32_t attributeMask = 0;
131 if (vertexBuffers.count < layout.numFormats) {
132 LOG_ERROR_ONCE(logger,
"vertexBufferCount=%u < inputLayout.numFormats=%zu", vertexBuffers.count, layout.numFormats);
136 for (
size_t b = 0; b < layout.numFormats; ++b) {
137 auto& vertexBuffer = vertexBuffers.currItems[b];
139 BufferGLES30& buffer = buffers->buffers[vertexBuffer.handle];
140 const VertexFormat* vertexFormat = layout.formats[b];
141 const GLsizei stride = vertexBuffer.stride ? vertexBuffer.stride : getSize(*vertexFormat);
142 const GLsizei bufferOffset =
static_cast<GLsizei
>(baseVertex) * stride + vertexBuffer.offset;
143 bindBuffer(OpenGLES30::BufferTarget::ArrayBuffer, buffer.bufferId);
144 for (
const VertexElement& element : vertexFormat->elements) {
146 GLint attributeLocation = -1;
147 for (
size_t i = 0; i < effect->activeAttributes; i++) {
148 if (effect->attributeSemantic[i].semantic == uint32_t(element.semantic) &&
149 effect->attributeSemantic[i].slot == element.semanticIndex)
151 attributeLocation = effect->attributeLocation[i];
155 if (attributeLocation == -1) {
160 const OpenGLES30::DataFormatInfo format = OpenGLES30::DataFormats[size_t(element.format)];
161 if (format.isVertexFormat == 0) {
162 LOG_WARNING_ONCE(logger,
"Encountered unsupported vertex format");
166 const GLvoid* attribOffset =
reinterpret_cast<const GLvoid*
>((size_t)element.offset + bufferOffset);
169 if (format.type == GL_FLOAT && format.components == 4 && format.columns == 4) {
170 for (GLuint a = 0; a < 4; a++) {
171 GLuint ix = attributeLocation + a;
172 glVertexAttribPointer(ix, 4, format.type, GL_TRUE, stride,
reinterpret_cast<const GLvoid*
>(
reinterpret_cast<size_t>(attribOffset) +
sizeof(GLfloat) * a * 4));
173 glEnableVertexAttribArray(ix);
174 glVertexAttribDivisor(ix, element.instanceStep);
175 attributeMask |= 1 << ix;
181 if (format.columns == 1) {
182 if (format.isInteger) {
183 glVertexAttribIPointer(attributeLocation, format.components, format.type, stride, attribOffset);
186 glVertexAttribPointer(attributeLocation, format.components, format.type, format.isNormalized ? GL_TRUE : GL_FALSE, stride, attribOffset);
188 glEnableVertexAttribArray(attributeLocation);
189 glVertexAttribDivisor(attributeLocation, element.instanceStep);
190 attributeMask |= 1 << attributeLocation;
196 vertexBuffers.baseVertex =
static_cast<uint32_t
>(baseVertex);
199 uint32_t disable = currentAttributeMask & (~attributeMask);
202 DWORD attributeIndex = 0;
203 _BitScanForward(&attributeIndex, disable);
205 uint32_t attributeIndex = __builtin_ctz(disable);
207 assert((1 << attributeIndex) & disable);
208 disable &= ~(1 << attributeIndex);
209 glDisableVertexAttribArray(attributeIndex);
211 currentAttributeMask = attributeMask;
214 inputLayout.prevHandle = inputLayout.currHandle;
216 needVertexAttributeSetup =
false;
219GLenum Cogs::ContextGLES30::bindBuffer(OpenGLES30::BufferTarget target, GLuint buffer)
221 GLenum glTarget = glBufferTargets[size_t(target)];
222 if (bufferTargets[
size_t(target)].buffer != buffer) {
223 bufferTargets[size_t(target)].buffer = buffer;
224 glBindBuffer(glTarget, buffer);
229void Cogs::ContextGLES30::unbindBuffer(GLuint buffer)
231 for (
size_t target = 0; target < size_t(OpenGLES30::BufferTarget::Count); target++) {
232 if (bufferTargets[target].buffer == buffer) {
233 glBindBuffer(glBufferTargets[target], 0);
234 bufferTargets[target].buffer = ~0u;
237 for (
size_t index = 0; index < size_t(OpenGLES30::maxUniformBuffers); index++) {
238 if (uniformBufferTargets[index].buffer == buffer) {
239 glBindBufferBase(GL_UNIFORM_BUFFER, GLuint(index), 0);
240 uniformBufferTargets[index].buffer = ~0u;
245void Cogs::ContextGLES30::unbindVAO()
249 glBindVertexArray(0);
251 vertexArrayObject.indexType = GL_INVALID_ENUM;
252 vertexArrayObject.indexCount = 0;
253 vertexArrayObject.indexStride = 0;
254 bufferTargets[size_t(OpenGLES30::BufferTarget::ArrayBuffer)].buffer = ~0u;
255 bufferTargets[size_t(OpenGLES30::BufferTarget::ElementArrayBuffer)].buffer = ~0u;
263 LOG_ERROR_ONCE(logger,
"setIndexBuffer: Cannot bind index buffer with non-zero offset");
267 if (HandleIsValid(indexBufferHandle)) {
268 BufferGLES30& ixbuf = buffers->buffers[indexBufferHandle];
270 LOG_ERROR_ONCE(logger,
"setIndexBuffer: Cannot bind non-index buffer as index buffer.");
276 indexBuffer.
handle = indexBufferHandle;
277 indexBuffer.stride = stride;
278 indexBuffer.offset = offset;
279 needVertexAttributeSetup =
true;
284 if (!HandleIsValid(bufferBindingHandle))
return;
285 if (!HandleIsValid(currentEffect.handle)) {
286 LOG_ERROR_ONCE(logger,
"setConstantBuffer: Invalid effect handle");
289 assert(currentEffect.ptr);
291 if (!HandleIsValid(bufferHandle)) {
292 LOG_ERROR_ONCE(logger,
"setConstantBuffer: Invalid buffer handle");
295 BufferGLES30& buffer = this->buffers->buffers[bufferHandle];
296 const GLuint index =
static_cast<GLuint
>(bufferBindingHandle.
handle & 0xFFFFFFFF) - 1;
297 assert(index < OpenGLES30::maxUniformBuffers);
298 if ((uniformBufferTargets[index].buffer != buffer.
bufferId) ||
299 (uniformBufferTargets[index].offset != offset) ||
300 (uniformBufferTargets[index].size != size))
302 uniformBufferTargets[index].buffer = buffer.
bufferId;
303 uniformBufferTargets[index].offset = offset;
304 uniformBufferTargets[index].size = size;
305 if (offset == 0 && size == 0xffffffffu) {
306 glBindBufferBase(GL_UNIFORM_BUFFER, index, buffer.
bufferId);
309 GLsizeiptr _size = GLsizeiptr(size);
310 if (_size == (GLsizeiptr)0xffffffff) {
311 _size = GLsizeiptr(buffer.
size - std::min(buffer.
size, offset));
313 glBindBufferRange(GL_UNIFORM_BUFFER, index, buffer.
bufferId, offset, _size);
318bool Cogs::ContextGLES30::setupState()
320 if (currBlendState.handle != nextBlendState.handle) {
323 if (HandleIsValid(nextBlendState.handle)) {
324 const BlendStateGLES30& blendState = renderTargets->blendStates[nextBlendState.handle];
326 if (currBlendState.state.enabled != blendState.enabled) {
327 currBlendState.state.enabled = blendState.enabled;
328 if (currBlendState.state.enabled) {
336 if (currBlendState.state.enabled) {
338 if ((currBlendState.state.blend.colorSrc != blendState.blend.colorSrc) ||
339 (currBlendState.state.blend.colorDst != blendState.blend.colorSrc) ||
340 (currBlendState.state.blend.alphaSrc != blendState.blend.alphaSrc) ||
341 (currBlendState.state.blend.alphaDst != blendState.blend.alphaDst))
343 currBlendState.state.blend = blendState.blend;
344 glBlendFuncSeparate(Blends[
size_t(currBlendState.state.blend.colorSrc)],
345 Blends[
size_t(currBlendState.state.blend.colorDst)],
346 Blends[
size_t(currBlendState.state.blend.alphaSrc)],
347 Blends[
size_t(currBlendState.state.blend.alphaDst)]);
350 if ((currBlendState.state.operation.color != blendState.operation.color) ||
351 (currBlendState.state.operation.alpha != blendState.operation.alpha))
353 currBlendState.state.operation = blendState.operation;
354 glBlendEquationSeparate(BlendOperations[
size_t(currBlendState.state.operation.color)],
355 BlendOperations[
size_t(currBlendState.state.operation.alpha)]);
358 if ((currBlendState.constant[0] != nextBlendState.constant[0]) ||
359 (currBlendState.constant[1] != nextBlendState.constant[1]) ||
360 (currBlendState.constant[2] != nextBlendState.constant[2]) ||
361 (currBlendState.constant[3] != nextBlendState.constant[3]))
363 for (
size_t i = 0; i < 4; i++) {
364 currBlendState.constant[i] = nextBlendState.constant[i];
366 glBlendColor(currBlendState.constant[0],
367 currBlendState.constant[1],
368 currBlendState.constant[2],
369 currBlendState.constant[3]);
373 else if (currBlendState.state.enabled) {
374 currBlendState.state.enabled =
false;
378 currBlendState.handle = nextBlendState.handle;
384bool Cogs::ContextGLES30::setupDraw(
size_t baseVertex)
392 LOG_ERROR_ONCE(logger,
"setupIndexedDraw: No valid effect.");
398 LOG_ERROR_ONCE(logger,
"setupIndexedDraw: Invalid vertex array object");
405 if (baseVertex != 0) {
406 LOG_ERROR_ONCE(logger,
"Unhandled VAO rendering with base vertex %zu != 0", baseVertex);
409 if (vertexArrayObject.prevHandle != vertexArrayObject.currHandle) {
410 vertexArrayObject.prevHandle = vertexArrayObject.currHandle;
411 VertexArrayObjectGLES30& vao = buffers->vertexArrayObjects[vertexArrayObject.currHandle];
412 glBindVertexArray(vao.glName);
413 vertexArrayObject.indexType = vao.indexType;
414 vertexArrayObject.indexCount = vao.indexCount;
415 vertexArrayObject.indexStride = vao.indexStride;
423 needVertexAttributeSetup = needVertexAttributeSetup || (vertexBuffers.baseVertex != baseVertex);
424 if (needVertexAttributeSetup) { setupVertexAttributes(baseVertex); }
430bool Cogs::ContextGLES30::setupIndexedDraw(GLenum& indexType, GLsizei& indexCount,
const GLvoid*& indexOffset,
const size_t startIndex,
const size_t numIndices,
const size_t baseVertex)
436 indexType = GL_INVALID_ENUM;
438 indexOffset =
nullptr;
442 LOG_ERROR_ONCE(logger,
"setupIndexedDraw: No valid effect.");
448 LOG_ERROR_ONCE(logger,
"setupIndexedDraw: Invalid vertex array object");
455 if (vertexArrayObject.prevHandle != vertexArrayObject.currHandle) {
456 VertexArrayObjectGLES30& vao = buffers->vertexArrayObjects[vertexArrayObject.currHandle];
457 if (!((vao.indexType == GL_UNSIGNED_SHORT) || (vao.indexType == GL_UNSIGNED_INT))) {
458 LOG_ERROR_ONCE(logger,
"setupIndexedDraw: Vertex array object does not contain an index buffer");
461 vertexArrayObject.prevHandle = vertexArrayObject.currHandle;
462 glBindVertexArray(vao.glName);
463 vertexArrayObject.indexType = vao.indexType;
464 vertexArrayObject.indexCount = vao.indexCount;
465 vertexArrayObject.indexStride = vao.indexStride;
468 const size_t stride = vertexArrayObject.indexStride ? vertexArrayObject.indexStride : (vertexArrayObject.indexType == GL_UNSIGNED_INT ?
sizeof(GLuint) :
sizeof(GLushort));
469 indexType = vertexArrayObject.indexType;
470 indexCount =
static_cast<GLsizei
>(numIndices ? numIndices : vertexArrayObject.indexCount);
471 indexOffset =
reinterpret_cast<const GLvoid*
>(startIndex * stride);
477 needVertexAttributeSetup = needVertexAttributeSetup || (vertexBuffers.baseVertex != baseVertex);
478 if (needVertexAttributeSetup) { setupVertexAttributes(baseVertex); }
480 if (!indexBuffer.handle) {
481 LOG_ERROR_ONCE(logger,
"setupIndexedDraw: No index buffer bound.");
485 const auto& buffer = buffers->buffers[indexBuffer.handle];
486 bindBuffer(OpenGLES30::BufferTarget::ElementArrayBuffer, buffer.bufferId);
487 const size_t stride = indexBuffer.stride ? indexBuffer.stride : (indexType == GL_UNSIGNED_INT ?
sizeof(GLuint) :
sizeof(GLushort));
488 indexType = buffer.indexType;
489 indexCount =
static_cast<GLsizei
>(numIndices ? numIndices : buffer.size / stride);
490 indexOffset =
reinterpret_cast<const GLvoid*
>(startIndex * stride);
498 if (!setupDraw(0))
return;
500 const GLenum glPrimitiveType = OpenGLES30::PrimitiveFormats[
static_cast<uint32_t
>(primitiveType)];
501 glDrawArrays(currentRasterizerState.wireFrame ? GL_LINES : glPrimitiveType,
static_cast<GLint
>(startVertex),
static_cast<GLsizei
>(numVertexes));
503 if (frameStatisticsEnabled) { frameStatisticsAccountDrawCall(numVertexes,
false); }
508 GLenum indexType = GL_INVALID_ENUM;
509 GLsizei indexCount = 0;
510 const GLvoid* indexOffset =
nullptr;
511 if (!setupIndexedDraw(indexType, indexCount, indexOffset, startIndex, numIndices, baseVertex))
return;
513 const GLenum glPrimitiveType = OpenGLES30::PrimitiveFormats[
static_cast<uint32_t
>(primitiveType)];
514 glDrawElements(currentRasterizerState.wireFrame ? GL_LINES : glPrimitiveType, indexCount, indexType, indexOffset);
516 if (frameStatisticsEnabled) { frameStatisticsAccountDrawCall(numIndices,
true); }
521 if (startInstance != 0) {
522 LOG_ERROR_ONCE(logger,
"drawInstanced: GLES30 does not support nonzero start instance");
526 if (!setupDraw(0))
return;
528 const GLenum glPrimitiveType = OpenGLES30::PrimitiveFormats[
static_cast<uint32_t
>(primitiveType)];
529 glDrawArraysInstanced(currentRasterizerState.wireFrame ? GL_LINES : glPrimitiveType,
static_cast<GLint
>(startVertex),
static_cast<GLsizei
>(numVertexes),
static_cast<GLsizei
>(numInstances));
531 if (frameStatisticsEnabled) { frameStatisticsAccountDrawCall(numVertexes,
false); }
536 if (startInstance != 0) {
537 LOG_ERROR_ONCE(logger,
"drawInstancedIndexed: GLES30 does not support nonzero start instance");
541 GLenum indexType = GL_INVALID_ENUM;
542 GLsizei indexCount = 0;
543 const GLvoid* indexOffset =
nullptr;
544 if (!setupIndexedDraw(indexType, indexCount, indexOffset, startIndex, numIndices, 0))
return;
546 const GLenum glPrimitiveType = OpenGLES30::PrimitiveFormats[
static_cast<uint32_t
>(primitiveType)];
547 glDrawElementsInstanced(currentRasterizerState.wireFrame ? GL_LINES : glPrimitiveType, indexCount, indexType, indexOffset,
static_cast<GLsizei
>(numInstances));
549 if (frameStatisticsEnabled) { frameStatisticsAccountDrawCall(numIndices,
true); }
555 vertexBuffers.count = uint32_t(count);
556 for (
size_t i = 0; i < count; i++) {
557 vertexBuffers.currItems[i].handle = handles[i];
558 vertexBuffers.currItems[i].stride = 0;
559 vertexBuffers.currItems[i].offset = 0;
561 needVertexAttributeSetup =
true;
567 vertexBuffers.count = uint32_t(count);
568 for (
size_t i = 0; i < count; i++) {
569 vertexBuffers.currItems[i].handle = handles[i];
570 vertexBuffers.currItems[i].stride = strides[i];
571 vertexBuffers.currItems[i].offset = offsets ? offsets[i] : 0;
573 needVertexAttributeSetup =
true;
578 vertexArrayObject.currHandle = vertexArrayObjectHandle;
579 needVertexAttributeSetup =
true;
584 nextBlendState.handle = handle;
585 for (
size_t i = 0; i < 4; i++) {
586 nextBlendState.constant[i] = constant ? constant[i] : 0.f;
592 assert(!inRenderPass);
593 if (renderTargets->bindRenderTargets(info.renderTargetHandle, info.depthStencilHandle)) {
594 currentRenderTarget = info.renderTargetHandle;
595 currentDepthStencilTarget = info.depthStencilHandle;
600 glBindFramebuffer(GL_FRAMEBUFFER, 0);
603 auto & renderTarget = renderTargets->renderTargets[currentRenderTarget];
604 for (
int i = 0; i < renderTarget.numViews; ++i) {
605 if(info.loadOp[i] == LoadOp::Clear){
606 glClearBufferfv(GL_COLOR, i, info.clearValue[i]);
610 if(info.depthLoadOp == LoadOp::Clear){
611 clearDepth(info.depthClearValue);
614 renderPassInfo = info;
619 assert(inRenderPass);
620 if(HandleIsValid(renderPassInfo.renderTargetHandle)){
621 RenderTargetGLES30 &target = renderTargets->renderTargets[renderPassInfo.renderTargetHandle];
622 for(
size_t i=0; i<target.numViews; i++){
623 if(HandleIsValid(renderPassInfo.resolveHandle[i])){
637 inRenderPass =
false;
643 if (currentRenderTarget != renderTargetHandle || currentDepthStencilTarget != depthStencilHandle) {
645 if (renderTargets->bindRenderTargets(renderTargetHandle, depthStencilHandle)) {
646 currentRenderTarget = renderTargetHandle;
647 currentDepthStencilTarget = depthStencilHandle;
652 glBindFramebuffer(GL_FRAMEBUFFER, 0);
660 glViewport(
static_cast<GLsizei
>(x),
static_cast<GLsizei
>(y),
static_cast<GLsizei
>(width),
static_cast<GLsizei
>(height));
665 glScissor(x, y, width, height);
670 size_t bufferCount = 1;
671 if (HandleIsValid(currentRenderTarget)) {
673 bufferCount = renderTarget.numViews;
675 for (
size_t i = 0; i < bufferCount; i++) {
676 glClearBufferfv(GL_COLOR, GLint(i), color);
682 if (count < 1)
return;
684 if (HandleIsValid(currentRenderTarget)) {
687 GLint n = std::min(GLint(renderTarget.numViews), GLint(count));
689 for (GLint i = 0; i < n; ++i) {
690 switch (renderTarget.viewData[i].basicType) {
694 glClearBufferfv(GL_COLOR, i, colors[i]);
697 uint32_t ucolors[4] = {
698 static_cast<uint32_t
>(colors[i][0]),
699 static_cast<uint32_t
>(colors[i][1]),
700 static_cast<uint32_t
>(colors[i][2]),
701 static_cast<uint32_t
>(colors[i][3])
703 glClearBufferuiv(GL_COLOR, i, ucolors);
707 int32_t icolors[4] = {
708 static_cast<int32_t
>(colors[i][0]),
709 static_cast<int32_t
>(colors[i][1]),
710 static_cast<int32_t
>(colors[i][2]),
711 static_cast<int32_t
>(colors[i][3])
713 glClearBufferiv(GL_COLOR, i, icolors);
719 glClearBufferfv(GL_COLOR, 0, colors[0]);
725 glDepthMask(GL_TRUE);
726 glClearBufferfv(GL_DEPTH, 0, &depth);
732 if (currentEffect.handle == effectHandle)
return;
735 LOG_ERROR_ONCE(logger,
"Cannot set invalid effect.");
737 currentEffect.ptr =
nullptr;
740 currentEffect.
handle = effectHandle;
742 currentEffect.ptr = &effects->effects[effectHandle];
743 glUseProgram(currentEffect.ptr->programId);
746 currentEffect.ptr =
nullptr;
749 needVertexAttributeSetup =
true;
755 if (!HandleIsValid(currentEffect.handle)) {
756 LOG_ERROR_ONCE(logger,
"setTexture: Current effect handle is invalid");
763 LOG_ERROR_ONCE(logger,
"setTexture: Texture binding handle is invalid");
767 GLint unit = GLint(textureBindingHandle.
handle - 1);
768 assert(0 <= unit && unit < GLint(OpenGLES30::maxTexUnits));
769 bindTexture(textureHandle, unit);
774 if (!HandleIsValid(currentEffect.handle) || !HandleIsValid(textureBindingHandle) || !HandleIsValid(textureViewHandle))
return;
778 GLint unit = GLint(textureBindingHandle.
handle - 1);
779 assert(0 <= unit && unit < GLint(OpenGLES30::maxTexUnits));
783 glTexParameteri(texture->target, GL_TEXTURE_BASE_LEVEL, view.
levelIndex);
784 glTexParameteri(texture->target, GL_TEXTURE_MAX_LEVEL, view.
levelIndex + view.
numLevels - 1);
789Cogs::TextureGLES30* Cogs::ContextGLES30::bindTexture(TextureHandle textureHandle, GLuint unit)
791 if (unit == GLuint(~0u)) {
792 unit = activeTexUnit;
794 assert(unit < OpenGLES30::maxTexUnits);
796 TextureGLES30* texture =
nullptr;
798 texture = &textures->textures[textureHandle];
800 if (texture && texture->target == GL_RENDERBUFFER) {
801 LOG_ERROR_ONCE(logger,
"Cannot bind a renderBuffer as a texture sampler");
806 if (texUnits[unit].texture == textureHandle && (texture ==
nullptr || texture->boundAsView == 0)) {
811 if (activeTexUnit != unit) {
812 glActiveTexture(GL_TEXTURE0 + unit);
813 activeTexUnit = unit;
820 if (
HandleIsValid(texUnits[unit].texture) && texUnits[unit].target != texture->target) {
821 glBindTexture(texUnits[unit].target, 0);
824 glBindTexture(texture->target, texture->textureId);
825 texUnits[unit].target = texture->target;
826 texUnits[unit].texture = textureHandle;
829 if (texture->boundAsView) {
830 glTexParameteri(texture->target, GL_TEXTURE_BASE_LEVEL, 0);
831 glTexParameteri(texture->target, GL_TEXTURE_MAX_LEVEL, 1000);
832 texture->boundAsView = 0;
840 LOG_WARNING_ONCE(logger,
"setTexture: Invalid texture handle");
844 glBindTexture(texUnits[unit].target, 0);
846 texUnits[unit].target = GL_TEXTURE_2D;
854 if (!samplerStateBindingHandle)
return;
856 const GLint unit =
static_cast<GLint
>(samplerStateBindingHandle.
handle - 1);
857 assert(0 <= unit && unit < GLint(OpenGLES30::maxTexUnits));
859 if (texUnits[unit].sampler != samplerStateHandle) {
860 texUnits[unit].sampler = samplerStateHandle;
861 if (HandleIsValid(samplerStateHandle)) {
862 const SamplerGLES30& sampler = textures->samplers[samplerStateHandle];
863 glBindSampler(unit, sampler.glName);
866 glBindSampler(unit, 0);
879 glFrontFace(currentRasterizerState.frontCounterClockwise ? GL_CCW : GL_CW);
882 if (currentRasterizerState.cullMode != rasterizerState.
cullMode) {
883 currentRasterizerState.cullMode = rasterizerState.
cullMode;
887 glDisable(GL_CULL_FACE);
891 glEnable(GL_CULL_FACE);
892 glCullFace(GL_FRONT);
896 glEnable(GL_CULL_FACE);
902 if (currentRasterizerState.noDepthClip != rasterizerState.
noDepthClip) {
903 currentRasterizerState.noDepthClip = rasterizerState.
noDepthClip;
904 if (capabilities->getDeviceCapabilities().NoDepthClip) {
905 if (currentRasterizerState.noDepthClip) {
906 glEnable(GL_DEPTH_CLAMP_EXT);
909 glDisable(GL_DEPTH_CLAMP_EXT);
914 if ((currentRasterizerState.depthBias != rasterizerState.
depthBias) ||
917 currentRasterizerState.depthBias = rasterizerState.
depthBias;
919 if ((currentRasterizerState.depthBias != 0.f) || (currentRasterizerState.slopeScaledDepthBias != 0.f))
921 glEnable(GL_POLYGON_OFFSET_FILL);
922 glPolygonOffset(currentRasterizerState.slopeScaledDepthBias, currentRasterizerState.depthBias);
925 glPolygonOffset(0, 0);
926 glDisable(GL_POLYGON_OFFSET_FILL);
930 if (currentRasterizerState.scissor != rasterizerState.
scissor) {
931 currentRasterizerState.scissor = rasterizerState.
scissor;
932 if (currentRasterizerState.scissor) {
933 glEnable(GL_SCISSOR_TEST);
936 glDisable(GL_SCISSOR_TEST);
943 inputLayout.currHandle = handle;
948 TextureGLES30 & sourceTexture = this->textures->textures[sourceTextureHandle];
949 TextureGLES30 & targetTexture = this->textures->textures[targetTextureHandle];
951 if (1 < targetTexture.numSamples) {
952 LOG_ERROR_ONCE(logger,
"Cannot resolve to multisample target");
955 if (sourceTexture.width != targetTexture.width || sourceTexture.height != targetTexture.height) {
956 LOG_ERROR_ONCE(logger,
"Cannot resolve to texture of different size");
959 if (targetTexture.textureId == 0) {
960 LOG_ERROR_ONCE(logger,
"Cannot resolve to null texture");
965 if (sourceTexture.fbo) {
966 glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceTexture.fbo);
968 else if (!setupFramebuffer(sourceTexture, GL_READ_FRAMEBUFFER)) {
973 if (targetTexture.fbo) {
974 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetTexture.fbo);
976 else if (!setupFramebuffer(targetTexture, GL_DRAW_FRAMEBUFFER)) {
983 glClear(GL_DEPTH_BUFFER_BIT);
984 glBlitFramebuffer(0, 0, sourceTexture.width, sourceTexture.height,
985 0, 0, targetTexture.width, targetTexture.height,
986 GL_DEPTH_BUFFER_BIT, GL_NEAREST);
989 glClear(GL_COLOR_BUFFER_BIT);
990 glBlitFramebuffer(0, 0, sourceTexture.width, sourceTexture.height,
991 0, 0, targetTexture.width, targetTexture.height,
992 GL_COLOR_BUFFER_BIT, GL_NEAREST);
994 glBindFramebuffer(GL_FRAMEBUFFER, 0);
997void Cogs::ContextGLES30::setDefaults()
1000 emptyUniformBuffer = buffers->loadBuffer(
nullptr,
1009 currentRasterizerState.frontCounterClockwise =
false;
1011 currentRasterizerState.depthBias = 0.0;
1012 currentRasterizerState.slopeScaledDepthBias = 0.0;
1013 currentRasterizerState.scissor =
false;
1014 currentRasterizerState.wireFrame =
false;
1015 currentRasterizerState.noDepthClip =
false;
1017 glDisable(GL_CULL_FACE);
1018 glDisable(GL_POLYGON_OFFSET_FILL);
1019 glDisable(GL_SCISSOR_TEST);
1020 if (capabilities->getDeviceCapabilities().NoDepthClip) {
1021 glDisable(GL_DEPTH_CLAMP_EXT);
1026 currBlendState.state.blend = { BlendState::Blend::One, BlendState::Blend::Zero, BlendState::Blend::One, BlendState::Blend::Zero };
1027 currBlendState.state.operation = { BlendState::BlendOperation::Add, BlendState::BlendOperation::Add };
1028 currBlendState.state.enabled =
false;
1030 glDisable(GL_BLEND);
1031 glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
1032 glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
1033 glBlendColor(0.f, 0.f, 0.f, 0.f);
1037 currentDepthStencilState.depthEnabled =
true;
1038 currentDepthStencilState.writeEnabled =
true;
1040 glEnable(GL_DEPTH_TEST);
1041 glDepthMask(GL_TRUE);
1042 glDepthFunc(GL_LESS);
1045 glActiveTexture(GL_TEXTURE0);
1049 glBindVertexArray(0);
1057 currentEffect.ptr =
nullptr;
1060 for (
size_t i = 0; i < size_t(OpenGLES30::BufferTarget::Count); i++) {
1061 glBindBuffer(glBufferTargets[i], 0);
1062 bufferTargets[i].buffer = 0;
1065 for (GLuint i = 0; i < OpenGLES30::maxUniformBuffers; i++) {
1066 if (uniformBufferTargets[i].buffer) {
1067 glBindBufferBase(GL_UNIFORM_BUFFER, i, 0);
1069 uniformBufferTargets[i].buffer = 0;
1070 uniformBufferTargets[i].offset = 0;
1071 uniformBufferTargets[i].size = ~0u;
1077 const BufferGLES30& buffer = buffers->buffers[emptyUniformBuffer];
1078 glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer.bufferId);
1085 for (GLuint i = 0; i < OpenGLES30::maxTexUnits; i++) {
1087 glActiveTexture(GL_TEXTURE0 + i);
1088 glBindTexture(texUnits[i].target, 0);
1089 texUnits[i].target = GL_TEXTURE_2D;
1092 glBindSampler(i, 0);
1097 glActiveTexture(GL_TEXTURE0);
1101 glBindVertexArray(0);
1103 for (GLenum i = 0; i < OpenGLES30::maxVertexAttributes; i++) {
1105 if (currentAttributeMask & (1 << i)) {
1106 glDisableVertexAttribArray(i);
1109 vertexBuffers.count = 0;
1110 currentAttributeMask = 0;
1113 currentEffect.ptr =
nullptr;
1121 if (currentDepthStencilState.depthEnabled != depthStencilState.
depthEnabled) {
1123 if (currentDepthStencilState.depthEnabled) {
1124 glEnable(GL_DEPTH_TEST);
1127 glDisable(GL_DEPTH_TEST);
1131 if (currentDepthStencilState.depthEnabled) {
1134 if (currentDepthStencilState.writeEnabled != depthStencilState.
writeEnabled) {
1135 currentDepthStencilState.writeEnabled = depthStencilState.
writeEnabled;
1136 if (currentDepthStencilState.writeEnabled) {
1137 glDepthMask(GL_TRUE);
1140 glDepthMask(GL_FALSE);
1145 if (currentDepthStencilState.depthFunction != depthStencilState.
depthFunction) {
1146 currentDepthStencilState.depthFunction = depthStencilState.
depthFunction;
1147 glDepthFunc(DepthFunctions[currentDepthStencilState.depthFunction]);
1154 LOG_ERROR_ONCE(logger,
"Reading of depth buffer is not supported");
1160 if (!HandleIsValid(bufferHandle))
return;
1161 BufferGLES30& buffer = this->buffers->buffers[bufferHandle];
1163 size_t elementSize = 4;
1164 GLenum source = GL_BACK;
1165 GLenum format = GL_RGBA;
1166 GLenum type = GL_UNSIGNED_BYTE;
1169 LOG_ERROR_ONCE(logger,
"Reading color buffer of invalid render target");
1173 if (HandleIsValid(currentRenderTarget)) {
1174 const RenderTargetGLES30& renderTarget = this->renderTargets->renderTargets[this->currentRenderTarget];
1176 source = GL_COLOR_ATTACHMENT0;
1178 if (renderTarget.numViews && HandleIsValid(renderTarget.views[0].
texture)) {
1180 elementSize = getFormatInfo(texture.format)->
blockSize;
1181 format = OpenGLES30::DataFormats[size_t(texture.format)].format;
1182 type = OpenGLES30::DataFormats[size_t(texture.format)].type;
1186 if (buffer.
size < elementSize * width * height) {
1187 LOG_ERROR_ONCE(logger,
"Color buffer read is larger than buffer size.");
1191 glReadBuffer(source);
1192 bindBuffer(OpenGLES30::BufferTarget::PixelPackBuffer, buffer.
bufferId);
1193 glReadPixels(x, y, width, height, format, type,
nullptr);
1198 if (!HandleIsValid(bufferHandle))
return nullptr;
1201 BufferGLES30& buffer = this->buffers->buffers[bufferHandle];
1203 LOG_ERROR_ONCE(logger,
"Buffers can only be mapped once at a time");
1218 GLenum glTarget = bindBuffer(OpenGLES30::BufferTarget::CopyReadBuffer, buffer.
bufferId);
1219 emscripten_glGetBufferSubData(glTarget, 0, buffer.
size, buffer.
mappedData.data());
1224 const void* ptr = glMapBufferRange(glTarget, 0, GLsizei(buffer.
size), GL_MAP_READ_BIT);
1228 glUnmapBuffer(glTarget);
1241 BufferGLES30& buffer = this->buffers->buffers[bufferHandle];
1244 LOG_ERROR_ONCE(logger,
"Unmapping buffer that is not mapped");
1249 GLenum glTarget = bindBufferCopy(OpenGLES30::BufferTarget::CopyWriteBuffer, buffer);
1251 if(uploadStatisticsEnabled) uploadStatisticsBufferUpload(buffer.
mappedData.size());
1263 if (!HandleIsValid(bufferHandle)) {
1264 LOG_ERROR_ONCE(logger,
"updateSubBuffer: invalid handle");
1268 GLenum glTarget = bindBufferCopy(OpenGLES30::BufferTarget::CopyWriteBuffer, buffer);
1269 if (size < buffer.
size) {
1271 glBufferSubData(glTarget, 0, buffer.
size,
nullptr);
1272 glBufferSubData(glTarget, 0,
static_cast<GLsizei
>(size), data);
1275 glBufferSubData(glTarget, 0, buffer.
size, data);
1277 if(uploadStatisticsEnabled) uploadStatisticsBufferUpload(buffer.
size);
1286 if (fmt.isTextureFormat && fmt.isCompressed == 0) {
1287 glTexSubImage2D(texture->target,
static_cast<GLint
>(level), 0, 0, texture->width, texture->height, fmt.format, fmt.type, data);
1288 if(uploadStatisticsEnabled) uploadStatisticsTextureUpload(texture->estimatedByteSize);
1293void Cogs::ContextGLES30::copyTexture(
TextureHandle dstHandle,
unsigned dstSub,
unsigned dstX,
unsigned dstY,
unsigned dstZ,
TextureHandle srcHandle,
unsigned srcSub)
1297 LOG_ERROR_ONCE(logger,
"copyTexture: invalid destination texture");
1301 if (!HandleIsValid(srcHandle)) {
1302 LOG_ERROR_ONCE(logger,
"copyTexture: invalid source texture");
1306 bool checkCompleteness =
false;
1309 TextureGLES30& srcTexture = textures->textures[srcHandle];
1310 if (srcTexture.textureId == 0) {
1311 LOG_ERROR_ONCE(logger,
"copyTexture: Cannot copy from null texture");
1315 if (srcTexture.target == GL_TEXTURE_2D && srcSub == 0) {
1316 if (srcTexture.fbo) {
1317 glBindFramebuffer(GL_READ_FRAMEBUFFER, srcTexture.fbo);
1320 glGenFramebuffers(1, &srcTexture.fbo);
1321 glBindFramebuffer(GL_READ_FRAMEBUFFER, srcTexture.fbo);
1322 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, srcTexture.textureId, 0);
1323 checkCompleteness =
true;
1327 else if (srcTexture.target == GL_TEXTURE_2D_ARRAY || srcTexture.target == GL_TEXTURE_3D) {
1330 glGenFramebuffers(1, &srcFbo);
1331 glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFbo);
1332 glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, srcTexture.textureId, 0, srcSub);
1333 checkCompleteness =
true;
1337 if (checkCompleteness) {
1338 GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
1339 if (status != GL_FRAMEBUFFER_COMPLETE) {
1340 LOG_ERROR_ONCE(logger,
"copyTexture: Failed to create source framebuffer: %s", OpenGLES30::framebuffersStatusString(status));
1341 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1349 if (dstTexture->target == GL_TEXTURE_2D) {
1350 glCopyTexSubImage2D(GL_TEXTURE_2D,
1353 0, 0, srcTexture.width, srcTexture.height);
1356 else if (dstTexture->target == GL_TEXTURE_2D_ARRAY || dstTexture->target == GL_TEXTURE_3D) {
1357 unsigned dstLevel = 0;
1358 unsigned dstLayer = dstSub;
1359 glCopyTexSubImage3D(dstTexture->target, dstLevel,
1360 dstX, dstY, dstZ + dstLayer,
1361 0, 0, srcTexture.width, srcTexture.height);
1366 LOG_ERROR_ONCE(logger,
"copyTexture: unsupported combination of source and destination texture");
1370 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1375 glDeleteFramebuffers(1, &srcFbo);
1385 if (!HandleIsValid(bufferHandle)) {
1386 LOG_ERROR_ONCE(logger,
"updateSubBuffer: invalid handle");
1390 if (buffer.
size < offset + size) {
1391 LOG_ERROR_ONCE(logger,
"updateSubBuffer: updating outside of buffer bounds");
1394 GLenum glTarget = bindBufferCopy(buffer.
target, buffer);
1395 glBufferSubData(glTarget,
static_cast<GLintptr
>(offset),
static_cast<GLsizei
>(size), data);
1396 if(uploadStatisticsEnabled) uploadStatisticsBufferUpload(size);
1426 if (!HandleIsValid(destinationHandle)) {
1427 LOG_ERROR_ONCE(logger,
"copyResource: destination buffer is invalid");
1430 BufferGLES30& dstBuffer = buffers->buffers[destinationHandle];
1431 GLenum dstTarget = bindBufferCopy(OpenGLES30::BufferTarget::CopyWriteBuffer, dstBuffer);
1433 if (!HandleIsValid(sourceHandle)) {
1434 LOG_ERROR_ONCE(logger,
"copyResource: source buffer is invalid");
1437 BufferGLES30& srcBuffer = buffers->buffers[sourceHandle];
1438 GLenum srcTarget = bindBuffer(OpenGLES30::BufferTarget::CopyReadBuffer, srcBuffer.bufferId);
1440 if (srcTarget == dstTarget) {
1441 LOG_ERROR_ONCE(logger,
"copyResource: Ending up copying from and to the same target (0x%04x)", srcTarget);
1445 glCopyBufferSubData(srcTarget, dstTarget, 0, 0, srcBuffer.size);
1448void Cogs::ContextGLES30::copyResource(TextureHandle targetTextureHandle, TextureHandle sourceTextureHandle)
1451 resolveResource(sourceTextureHandle, targetTextureHandle);
Log implementation class.
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Framebuffer
Framebuffers to select from when doing framebuffer operations.
PrimitiveType
Primitive types for interpreting vertex data sent to the graphics pipeline.
@ InstanceMatrix
Instance matrix semantic.
@ None
The buffer can not be either read from or written to by the CPU after creation.
@ ConstantBuffer
The buffer can be bound as input to effects as a constant buffer.
GLuint bufferId
OpenGL buffer name.
unsigned short isMapped
Buffer is currently mapped.
OpenGLES30::BufferTarget target
OpenGL bind target.
uint32_t size
Buffer size..
Memory::MemoryBuffer mappedData
Memory map backing store.
unsigned short isIndexBuffer
Buffer is an index buffer.
unsigned short keepMapBacking
Do not release memory mapping backing store after unmap, set to 1 for repeatedly mapped buffers.
unsigned short writeBackMap
Set by map, if one, write back results to GL after unmap (i.e. map with write flags).
void signal(FenceHandle fenceHandle) override
Insert a fence in the command stream that will signal when all commands before the fence are complete...
void setDepthStencilState(const DepthStencilStateHandle handle) override
Set the current depth stencil state.
void setViewport(const float x, const float y, const float width, const float height) override
Sets the current viewport to the given location and dimensions.
void setInputLayout(const InputLayoutHandle inputLayoutHandle) override
Sets the current input layout.
void drawInstanced(PrimitiveType primitiveType, const size_t startVertex, const size_t numVertexes, const size_t startInstance, const size_t numInstances) override
Draws non-indexed, instanced primitives.
void beginRenderPass(const RenderPassInfo &info) override
Begin a render pass.
void setSamplerState(const SamplerStateBindingHandle samplerStateBindingHandle, const SamplerStateHandle samplerStateHandle) override
Sets the sampler state binding given to the given sampler state.
void * map(BufferHandle bufferHandle, MapMode::EMapMode mapMode, uint32_t *stride=nullptr) override
Maps the given buffer so it can be accessed.
void setBuffer(const BufferBindingHandle bufferBindingHandle, BufferHandle bufferHandle) override
Sets a buffer to bind to the given binding.
void endRenderPass() override
End a render pass.
void setVertexArrayObject(VertexArrayObjectHandle vertexArrayObject) override
void updateSubTexture(TextureHandle textureHandle, const size_t level, const void *data) override
Update the data of a level in the given texture.
void unmap(BufferHandle bufferHandle) override
Unmaps the given buffer, applying any synchronization necessary to reflect changes in the mapped memo...
void setConstantBuffer(const ConstantBufferBindingHandle bufferBindingHandle, const BufferHandle bufferHandle, const uint32_t offset, const uint32_t size) override
Sets a constant buffer to the given constant buffer binding.
void updateSubBuffer(BufferHandle bufferHandle, const size_t offset, const size_t size, const void *data) override
Update a region of data in a buffer.
void readDepthBuffer(BufferHandle bufferHandle, int x, int y, int width, int height, Framebuffer framebuffer) override
Reads data from the current depth target into the given bufferHandle.
void setScissor(const int x, const int y, const int width, const int height) override
Sets the current scissor rectangle.
void setBlendState(const BlendStateHandle handle, const float *constant) override
Set the current blend state.
void setRasterizerState(const RasterizerStateHandle handle) override
Set the current rasterizer state.
void readColorBuffer(BufferHandle bufferHandle, int x, int y, int width, int height, Framebuffer framebuffer) override
Reads data from the current render target into the given bufferHandle.
void resolveResource(TextureHandle source, TextureHandle destination) override
Resolves the given source resource target into the given destination texture.
void reset() override
Resets all state changes made to the GPU since the last call to beginFrame.
void clearDepth(const float depth=1.0f) override
Clear the currently set depth/stencil target to the given depth.
void getBufferCounter(BufferHandle bufferHandle, BufferHandle destinationBufferHandle) override
Get the associated counter of a buffer.
void clearRenderTarget(const float *color) override
Clear the currently set render target to the given value (4 component floating point RGBA).
void setBufferCounter(BufferHandle bufferHandle, uint32_t value) override
Set the associated counter of a buffer.
void setIndexBuffer(IndexBufferHandle indexBufferHandle, uint32_t stride, uint32_t offset) override
Sets the current index buffer.
void drawInstancedIndexed(PrimitiveType primitiveType, const size_t startInstance, const size_t numInstances, const size_t startIndex, const size_t numIndexes) override
Draws indexed, instanced primitives.
void clearCachedState() final
Prepare context for external manipulation of graphics device.
void draw(PrimitiveType primitiveType, const size_t startVertex, const size_t numVertexes) override
Draws non-indexed, non-instanced primitives.
void drawIndexed(PrimitiveType primitiveType, const size_t startIndex, const size_t numIndexes, const size_t startVertex=0) override
Draws indexed, non-instanced primitives.
void setEffect(EffectHandle handle) override
Set the current effect.
void setVertexBuffers(const VertexBufferHandle *vertexBufferHandles, const size_t count, const uint32_t *strides, const uint32_t *offsets) override
Sets the current vertex buffers.
void setRenderTarget(const RenderTargetHandle handle, const DepthStencilHandle depthStencilHandle) override
Sets the current render target and an associated depth stencil target.
void updateBuffer(BufferHandle bufferHandle, const void *data, size_t size) override
Replace contents of buffer with new data.
Encapsulates state for depth buffer usage and stencil buffer usage in a state object.
bool depthEnabled
If depth testing is enabled/disabled. Default is true.
DepthFunction depthFunction
The depth function to use for depth testing.
static DepthStencilState DefaultState()
Constructs a depth stencil state object initialized with the default values.
bool writeEnabled
If writes to the depth buffer are enabled/disabled. Default is true.
Handle template class used to provide opaque, non-converting handles.
static const Handle_t NoHandle
Represents a handle to nothing.
handle_type handle
Internal resource handle.
static const Handle_t InvalidHandle
Represents an invalid handle.
Provides effects and shader management functionality.
EMapMode
Mapping mode enumeration.
@ WriteDiscard
Write access. When unmapping the graphics system will discard the old contents of the resource.
@ ReadWrite
Read and write access.
Encapsulates state for primitive rasterization in a state object.
static RasterizerState DefaultState()
Constructs a rasterizer state initialized with the default values.
bool scissor
Enable scissor rect.
bool frontCounterClockwise
If counter clockwise polygon winding is used to specify front facing polygons. Default is false.
bool noDepthClip
Clamp depth value instead of clipping against near and depth planes.
CullMode cullMode
Which face culling mode to apply to primitives before rasterization.
float depthBias
Depth bias to apply to depth values after initial rasterization before depth values are written.
float slopeScaledDepthBias
Slope scaled depth bias value controlling the depth bias value based on the area of the polygon on sc...
@ None
Do not perform any face culling.
@ Front
Cull front facing primitives.
bool wireFrame
If only wire frames should be rasterized. Default is false.
uint16_t layerIndex
Index of the first layer (if array) to render to.
uint16_t numLayers
Number of available layers to render to.
TextureHandle texture
Texture handle.
uint8_t levelIndex
Index of the mipmap level to render to.
@ DepthBuffer
The texture can be used as a depth target and have depth buffer values written into.
unsigned short boundAsView
True if last use of texture was as a view.
Describes how to fetch data from a texture in shaders.
uint32_t layerIndex
Index of the first layer (if array) to fetch from.
uint32_t numLayers
Number of array layers available.
uint32_t numLevels
Number of mipmap levels available.
uint32_t levelIndex
First mipmap level to fetch data from.
TextureHandle texture
Texture.
@ Static
Buffer will be loaded once and used to render many subsequent frames without any updates.