Example #1
0
bool WrappedOpenGL::Serialise_glSamplerParameteri(GLuint sampler, GLenum pname, GLint param)
{
  SERIALISE_ELEMENT(ResourceId, id, GetResourceManager()->GetID(SamplerRes(GetCtx(), sampler)));
  SERIALISE_ELEMENT(GLenum, PName, pname);

  int32_t ParamValue = 0;

  RDCCOMPILE_ASSERT(sizeof(int32_t) == sizeof(GLenum),
                    "int32_t isn't the same size as GLenum - aliased serialising will break");
  // special case a few parameters to serialise their value as an enum, not an int
  if(PName == GL_TEXTURE_WRAP_S || PName == GL_TEXTURE_WRAP_T || PName == GL_TEXTURE_WRAP_R ||
     PName == GL_TEXTURE_MIN_FILTER || PName == GL_TEXTURE_MAG_FILTER ||
     PName == GL_TEXTURE_COMPARE_MODE || PName == GL_TEXTURE_COMPARE_FUNC)
  {
    SERIALISE_ELEMENT(GLenum, Param, (GLenum)param);

    ParamValue = (int32_t)Param;
  }
  else
  {
    SERIALISE_ELEMENT(int32_t, Param, param);

    ParamValue = Param;
  }

  if(m_State < WRITING)
  {
    GLResource res = GetResourceManager()->GetLiveResource(id);
    m_Real.glSamplerParameteri(res.name, PName, ParamValue);
  }

  return true;
}
Example #2
0
bool WrappedOpenGL::Serialise_glPointParameteri(GLenum pname, GLint param)
{
  SERIALISE_ELEMENT(GLenum, PName, pname);

  int32_t ParamValue = 0;

  RDCCOMPILE_ASSERT(sizeof(int32_t) == sizeof(GLenum),
                    "int32_t isn't the same size as GLenum - aliased serialising will break");
  // special case a few parameters to serialise their value as an enum, not an int
  if(PName == GL_POINT_SPRITE_COORD_ORIGIN)
  {
    SERIALISE_ELEMENT(GLenum, Param, (GLenum)param);

    ParamValue = (int32_t)Param;
  }
  else
  {
    SERIALISE_ELEMENT(int32_t, Param, param);

    ParamValue = Param;
  }

  if(m_State <= EXECUTING)
  {
    m_Real.glPointParameteri(PName, ParamValue);
  }

  return true;
}
Example #3
0
std::string DoStringise(const D3D12ResourceBarrierSubresource &el)
{
  RDCCOMPILE_ASSERT(sizeof(D3D12ResourceBarrierSubresource) == sizeof(uint32_t),
                    "Enum isn't uint sized");

  if(el == D3D12AllSubresources)
    return "All Subresources";

  return ToStr(uint32_t(el));
}
Example #4
0
std::string DoStringise(const D3D12ComponentMapping &el)
{
  RDCCOMPILE_ASSERT(sizeof(D3D12ComponentMapping) == sizeof(uint32_t), "Enum isn't uint sized");

  std::string ret;

  // value should always be <= 5, see D3D12_SHADER_COMPONENT_MAPPING
  const char mapping[] = {'R', 'G', 'B', 'A', '0', '1', '?', '!'};

  uint32_t swizzle = (uint32_t)el;

  for(int i = 0; i < 4; i++)
    ret += mapping[D3D12_DECODE_SHADER_4_COMPONENT_MAPPING(i, swizzle)];

  return ret;
}
Example #5
0
VkResult WrappedVulkan::vkGetPipelineCacheData(VkDevice device, VkPipelineCache pipelineCache,
                                               size_t *pDataSize, void *pData)
{
  size_t totalSize = 16 + VK_UUID_SIZE + 4;    // required header (16+UUID) and 4 0 bytes

  if(pDataSize && !pData)
    *pDataSize = totalSize;

  if(pDataSize && pData)
  {
    if(*pDataSize < totalSize)
    {
      memset(pData, 0, *pDataSize);
      return VK_INCOMPLETE;
    }

    uint32_t *ptr = (uint32_t *)pData;

    ptr[0] = (uint32_t)totalSize;
    ptr[1] = VK_PIPELINE_CACHE_HEADER_VERSION_ONE;
    // just in case the user expects a valid vendorID/deviceID, write the real one
    // MULTIDEVICE need to get the right physical device for this device
    ptr[2] = m_PhysicalDeviceData.props.vendorID;
    ptr[3] = m_PhysicalDeviceData.props.deviceID;

    MakeFakeUUID();

    memcpy(ptr + 4, fakeRenderDocUUID, VK_UUID_SIZE);
    // [4], [5], [6], [7]

    RDCCOMPILE_ASSERT(VK_UUID_SIZE == 16, "VK_UUID_SIZE has changed");

    // empty bytes
    ptr[8] = 0;
  }

  // we don't want the application to use pipeline caches at all, and especially
  // don't want to return any data for future use. We thus return a technically
  // valid but empty pipeline cache. Our UUID changes every run so in theory the
  // application should never provide an old cache, but just in case we will nop
  // it out in create pipeline cache
  return VK_SUCCESS;
}
Example #6
0
MeshDisplayPipelines VulkanDebugManager::CacheMeshDisplayPipelines(VkPipelineLayout pipeLayout,
                                                                   const MeshFormat &primary,
                                                                   const MeshFormat &secondary)
{
  // generate a key to look up the map
  uint64_t key = 0;

  uint64_t bit = 0;

  if(primary.indexByteStride == 4)
    key |= 1ULL << bit;
  bit++;

  RDCASSERT((uint32_t)primary.topology < 64);
  key |= uint64_t((uint32_t)primary.topology & 0x3f) << bit;
  bit += 6;

  VkFormat primaryFmt = MakeVkFormat(primary.format);
  VkFormat secondaryFmt = secondary.vertexResourceId == ResourceId()
                              ? VK_FORMAT_UNDEFINED
                              : MakeVkFormat(secondary.format);

  RDCCOMPILE_ASSERT(VK_FORMAT_RANGE_SIZE <= 255,
                    "Mesh pipeline cache key needs an extra bit for format");

  key |= uint64_t((uint32_t)primaryFmt & 0xff) << bit;
  bit += 8;

  key |= uint64_t((uint32_t)secondaryFmt & 0xff) << bit;
  bit += 8;

  RDCASSERT(primary.vertexByteStride <= 0xffff);
  key |= uint64_t((uint32_t)primary.vertexByteStride & 0xffff) << bit;
  bit += 16;

  if(secondary.vertexResourceId != ResourceId())
  {
    RDCASSERT(secondary.vertexByteStride <= 0xffff);
    key |= uint64_t((uint32_t)secondary.vertexByteStride & 0xffff) << bit;
  }
  bit += 16;

  if(primary.instanced)
    key |= 1ULL << bit;
  bit++;

  if(secondary.instanced)
    key |= 1ULL << bit;
  bit++;

  // only 64 bits, make sure they all fit
  RDCASSERT(bit < 64);

  MeshDisplayPipelines &cache = m_CachedMeshPipelines[key];

  if(cache.pipes[(uint32_t)SolidShade::NoSolid] != VK_NULL_HANDLE)
    return cache;

  const VkLayerDispatchTable *vt = ObjDisp(m_Device);
  VkResult vkr = VK_SUCCESS;

  // should we try and evict old pipelines from the cache here?
  // or just keep them forever

  VkVertexInputBindingDescription binds[] = {
      // primary
      {0, primary.vertexByteStride,
       primary.instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX},
      // secondary
      {1, secondary.vertexByteStride,
       secondary.instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX}};

  RDCASSERT(primaryFmt != VK_FORMAT_UNDEFINED);

  VkVertexInputAttributeDescription vertAttrs[] = {
      // primary
      {
          0, 0, primaryFmt, 0,
      },
      // secondary
      {
          1, 0, primaryFmt, 0,
      },
  };

  VkPipelineVertexInputStateCreateInfo vi = {
      VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, NULL, 0, 1, binds, 2, vertAttrs,
  };

  VkPipelineShaderStageCreateInfo stages[3] = {
      {VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, NULL, 0, VK_SHADER_STAGE_ALL_GRAPHICS,
       VK_NULL_HANDLE, "main", NULL},
      {VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, NULL, 0, VK_SHADER_STAGE_ALL_GRAPHICS,
       VK_NULL_HANDLE, "main", NULL},
      {VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, NULL, 0, VK_SHADER_STAGE_ALL_GRAPHICS,
       VK_NULL_HANDLE, "main", NULL},
  };

  VkPipelineInputAssemblyStateCreateInfo ia = {
      VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, NULL, 0,
      primary.topology >= Topology::PatchList ? VK_PRIMITIVE_TOPOLOGY_POINT_LIST
                                              : MakeVkPrimitiveTopology(primary.topology),
      false,
  };

  VkRect2D scissor = {{0, 0}, {16384, 16384}};

  VkPipelineViewportStateCreateInfo vp = {
      VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, NULL, 0, 1, NULL, 1, &scissor};

  VkPipelineRasterizationStateCreateInfo rs = {
      VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
      NULL,
      0,
      false,
      false,
      VK_POLYGON_MODE_FILL,
      VK_CULL_MODE_NONE,
      VK_FRONT_FACE_CLOCKWISE,
      false,
      0.0f,
      0.0f,
      0.0f,
      1.0f,
  };

  VkPipelineMultisampleStateCreateInfo msaa = {
      VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
      NULL,
      0,
      VULKAN_MESH_VIEW_SAMPLES,
      false,
      0.0f,
      NULL,
      false,
      false};

  VkPipelineDepthStencilStateCreateInfo ds = {
      VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
      NULL,
      0,
      true,
      true,
      VK_COMPARE_OP_LESS_OR_EQUAL,
      false,
      false,
      {VK_STENCIL_OP_KEEP, VK_STENCIL_OP_KEEP, VK_STENCIL_OP_KEEP, VK_COMPARE_OP_ALWAYS, 0, 0, 0},
      {VK_STENCIL_OP_KEEP, VK_STENCIL_OP_KEEP, VK_STENCIL_OP_KEEP, VK_COMPARE_OP_ALWAYS, 0, 0, 0},
      0.0f,
      1.0f,
  };

  VkPipelineColorBlendAttachmentState attState = {
      false,
      VK_BLEND_FACTOR_ONE,
      VK_BLEND_FACTOR_ZERO,
      VK_BLEND_OP_ADD,
      VK_BLEND_FACTOR_ONE,
      VK_BLEND_FACTOR_ZERO,
      VK_BLEND_OP_ADD,
      0xf,
  };

  VkPipelineColorBlendStateCreateInfo cb = {
      VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
      NULL,
      0,
      false,
      VK_LOGIC_OP_NO_OP,
      1,
      &attState,
      {1.0f, 1.0f, 1.0f, 1.0f}};

  VkDynamicState dynstates[] = {VK_DYNAMIC_STATE_VIEWPORT};

  VkPipelineDynamicStateCreateInfo dyn = {
      VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
      NULL,
      0,
      ARRAY_COUNT(dynstates),
      dynstates,
  };

  VkRenderPass rp;    // compatible render pass

  {
    VkAttachmentDescription attDesc[] = {
        {0, VK_FORMAT_R8G8B8A8_SRGB, VULKAN_MESH_VIEW_SAMPLES, VK_ATTACHMENT_LOAD_OP_LOAD,
         VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
         VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
         VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL},
        {0, VK_FORMAT_D32_SFLOAT, VULKAN_MESH_VIEW_SAMPLES, VK_ATTACHMENT_LOAD_OP_LOAD,
         VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
         VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
         VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL},
    };

    VkAttachmentReference attRef = {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
    VkAttachmentReference dsRef = {1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL};

    VkSubpassDescription sub = {
        0,      VK_PIPELINE_BIND_POINT_GRAPHICS,
        0,      NULL,       // inputs
        1,      &attRef,    // color
        NULL,               // resolve
        &dsRef,             // depth-stencil
        0,      NULL,       // preserve
    };

    VkRenderPassCreateInfo rpinfo = {
        VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
        NULL,
        0,
        2,
        attDesc,
        1,
        &sub,
        0,
        NULL,    // dependencies
    };

    vt->CreateRenderPass(Unwrap(m_Device), &rpinfo, NULL, &rp);
  }

  VkGraphicsPipelineCreateInfo pipeInfo = {
      VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
      NULL,
      0,
      2,
      stages,
      &vi,
      &ia,
      NULL,    // tess
      &vp,
      &rs,
      &msaa,
      &ds,
      &cb,
      &dyn,
      Unwrap(pipeLayout),
      rp,
      0,                 // sub pass
      VK_NULL_HANDLE,    // base pipeline handle
      0,                 // base pipeline index
  };

  // wireframe pipeline
  stages[0].module = Unwrap(m_pDriver->GetShaderCache()->GetBuiltinModule(BuiltinShader::MeshVS));
  stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
  stages[1].module = Unwrap(m_pDriver->GetShaderCache()->GetBuiltinModule(BuiltinShader::MeshFS));
  stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;

  rs.polygonMode = VK_POLYGON_MODE_LINE;
  rs.lineWidth = 1.0f;
  ds.depthTestEnable = false;

  vkr = vt->CreateGraphicsPipelines(Unwrap(m_Device), VK_NULL_HANDLE, 1, &pipeInfo, NULL,
                                    &cache.pipes[MeshDisplayPipelines::ePipe_Wire]);
  RDCASSERTEQUAL(vkr, VK_SUCCESS);

  ds.depthTestEnable = true;

  vkr = vt->CreateGraphicsPipelines(Unwrap(m_Device), VK_NULL_HANDLE, 1, &pipeInfo, NULL,
                                    &cache.pipes[MeshDisplayPipelines::ePipe_WireDepth]);
  RDCASSERTEQUAL(vkr, VK_SUCCESS);

  // solid shading pipeline
  rs.polygonMode = VK_POLYGON_MODE_FILL;
  ds.depthTestEnable = false;

  vkr = vt->CreateGraphicsPipelines(Unwrap(m_Device), VK_NULL_HANDLE, 1, &pipeInfo, NULL,
                                    &cache.pipes[MeshDisplayPipelines::ePipe_Solid]);
  RDCASSERTEQUAL(vkr, VK_SUCCESS);

  ds.depthTestEnable = true;

  vkr = vt->CreateGraphicsPipelines(Unwrap(m_Device), VK_NULL_HANDLE, 1, &pipeInfo, NULL,
                                    &cache.pipes[MeshDisplayPipelines::ePipe_SolidDepth]);
  RDCASSERTEQUAL(vkr, VK_SUCCESS);

  if(secondary.vertexResourceId != ResourceId())
  {
    // pull secondary information from second vertex buffer
    vertAttrs[1].binding = 1;
    vertAttrs[1].format = secondaryFmt;
    RDCASSERT(secondaryFmt != VK_FORMAT_UNDEFINED);

    vi.vertexBindingDescriptionCount = 2;

    vkr = vt->CreateGraphicsPipelines(Unwrap(m_Device), VK_NULL_HANDLE, 1, &pipeInfo, NULL,
                                      &cache.pipes[MeshDisplayPipelines::ePipe_Secondary]);
    RDCASSERTEQUAL(vkr, VK_SUCCESS);
  }

  vertAttrs[1].binding = 0;
  vi.vertexBindingDescriptionCount = 1;

  // flat lit pipeline, needs geometry shader to calculate face normals
  stages[2].module = Unwrap(m_pDriver->GetShaderCache()->GetBuiltinModule(BuiltinShader::MeshGS));
  stages[2].stage = VK_SHADER_STAGE_GEOMETRY_BIT;
  pipeInfo.stageCount = 3;

  if(stages[2].module != VK_NULL_HANDLE)
  {
    vkr = vt->CreateGraphicsPipelines(Unwrap(m_Device), VK_NULL_HANDLE, 1, &pipeInfo, NULL,
                                      &cache.pipes[MeshDisplayPipelines::ePipe_Lit]);
    RDCASSERTEQUAL(vkr, VK_SUCCESS);
  }

  for(uint32_t i = 0; i < MeshDisplayPipelines::ePipe_Count; i++)
    if(cache.pipes[i] != VK_NULL_HANDLE)
      m_pDriver->GetResourceManager()->WrapResource(Unwrap(m_Device), cache.pipes[i]);

  vt->DestroyRenderPass(Unwrap(m_Device), rp, NULL);

  return cache;
}
Example #7
0
bool GLReplay::RenderTexture(TextureDisplay cfg)
{
	MakeCurrentReplayContext(m_DebugCtx);
	
	WrappedOpenGL &gl = *m_pDriver;
	
	gl.glUseProgram(DebugData.texDisplayProg);

	auto &texDetails = m_pDriver->m_Textures[cfg.texid];

	gl.glActiveTexture(eGL_TEXTURE0);
	gl.glBindTexture(eGL_TEXTURE_2D, texDetails.resource.name);

	if(cfg.mip == 0 && cfg.scale < 1.0f)
		gl.glBindSampler(0, DebugData.linearSampler);
	else
		gl.glBindSampler(0, DebugData.pointSampler);
	
	GLint tex_x = texDetails.width, tex_y = texDetails.height, tex_z = texDetails.depth;

	gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, DebugData.UBOs[0]);

	struct uboData
	{
		Vec2f Position;
		float Scale;
		float HDRMul;

		Vec4f Channels;

		float RangeMinimum;
		float InverseRangeSize;
		float MipLevel;
		float dummy2;
		
		Vec3f TextureResolutionPS;
		int   OutputDisplayFormat;
		
		Vec2f OutputRes;
		int   RawOutput;
		float Slice;
	};

	uboData *ubo = (uboData *)gl.glMapBufferRange(eGL_UNIFORM_BUFFER, 0, sizeof(uboData), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);

	RDCCOMPILE_ASSERT(sizeof(uboData) <= DebugData.UBOSize, "UBO data is too big");
	
	float x = cfg.offx;
	float y = cfg.offy;
	
	ubo->Position.x = x;
	ubo->Position.y = y;
	ubo->Scale = cfg.scale;
	
	if(cfg.scale <= 0.0f)
	{
		float xscale = DebugData.outWidth/float(tex_x);
		float yscale = DebugData.outHeight/float(tex_y);

		ubo->Scale = RDCMIN(xscale, yscale);

		if(yscale > xscale)
		{
			ubo->Position.x = 0;
			ubo->Position.y = (DebugData.outHeight-(tex_y*ubo->Scale) )*0.5f;
		}
		else
		{
			ubo->Position.y = 0;
			ubo->Position.x = (DebugData.outWidth-(tex_x*ubo->Scale) )*0.5f;
		}
	}

	ubo->HDRMul = cfg.HDRMul;
	
	if(cfg.rangemax <= cfg.rangemin) cfg.rangemax += 0.00001f;

	ubo->Channels.x = cfg.Red ? 1.0f : 0.0f;
	ubo->Channels.y = cfg.Green ? 1.0f : 0.0f;
	ubo->Channels.z = cfg.Blue ? 1.0f : 0.0f;
	ubo->Channels.w = cfg.Alpha ? 1.0f : 0.0f;

	ubo->RangeMinimum = cfg.rangemin;
	ubo->InverseRangeSize = 1.0f/(cfg.rangemax-cfg.rangemin);
	
	ubo->MipLevel = (float)cfg.mip;

	ubo->OutputDisplayFormat = 0x2; // 2d. Unused for now

	ubo->RawOutput = cfg.rawoutput ? 1 : 0;

	ubo->TextureResolutionPS.x = float(tex_x);
	ubo->TextureResolutionPS.y = float(tex_y);
	ubo->TextureResolutionPS.z = float(tex_z);

	ubo->OutputRes.x = DebugData.outWidth;
	ubo->OutputRes.y = DebugData.outHeight;

	gl.glUnmapBuffer(eGL_UNIFORM_BUFFER);

	if(cfg.rawoutput)
	{
		gl.glDisable(eGL_BLEND);
	}
	else
	{
		gl.glEnable(eGL_BLEND);
		gl.glBlendFunc(eGL_SRC_ALPHA, eGL_ONE_MINUS_SRC_ALPHA);
	}

	gl.glBindVertexArray(DebugData.emptyVAO);
	gl.glDrawArrays(eGL_TRIANGLE_STRIP, 0, 4);
	
	gl.glBindSampler(0, 0);

	return true;
}
Example #8
0
static void ForAllProgramUniforms(SerialiserType *ser, CaptureState state, const GLHookSet &gl,
                                  GLuint progSrc, GLuint progDst, map<GLint, GLint> *locTranslate)
{
  const bool ReadSourceProgram = CopyUniforms || (SerialiseUniforms && ser && ser->IsWriting());
  const bool WriteDestProgram = CopyUniforms || (SerialiseUniforms && ser && ser->IsReading());

  RDCCOMPILE_ASSERT((CopyUniforms && !SerialiseUniforms) || (!CopyUniforms && SerialiseUniforms),
                    "Invalid call to ForAllProgramUniforms");

  // this struct will be serialised with the uniform binding data, or if we're just copying it will
  // be used to store the data fetched from the source program, before being applied to the
  // destination program. It's slightly redundant since we could unify the loops (as the code used
  // to do) but it's much better for code organisation and clarity to have a single path whether
  // serialising or not.
  ProgramUniforms serialisedUniforms;

  // if we're reading the source program, iterate over the interfaces and fetch the data.
  if(CheckConstParam(ReadSourceProgram))
  {
    const size_t numProps = 5;
    GLenum resProps[numProps] = {
        eGL_BLOCK_INDEX, eGL_TYPE, eGL_NAME_LENGTH, eGL_ARRAY_SIZE, eGL_LOCATION,
    };
    GLint values[numProps];

    GLint NumUniforms = 0;
    gl.glGetProgramInterfaceiv(progSrc, eGL_UNIFORM, eGL_ACTIVE_RESOURCES, &NumUniforms);

    // this is a very conservative figure - many uniforms will be in UBOs and so will be ignored
    serialisedUniforms.ValueUniforms.reserve(NumUniforms);

    for(GLint i = 0; i < NumUniforms; i++)
    {
      GLenum type = eGL_NONE;
      int32_t arraySize = 0;
      int32_t srcLocation = 0;
      string basename;
      bool isArray = false;

      gl.glGetProgramResourceiv(progSrc, eGL_UNIFORM, i, numProps, resProps, numProps, NULL, values);

      // we don't need to consider uniforms within UBOs
      if(values[0] >= 0)
        continue;

      // get the metadata we need for fetching the data
      type = (GLenum)values[1];
      arraySize = values[3];
      srcLocation = values[4];

      char n[1024] = {0};
      gl.glGetProgramResourceName(progSrc, eGL_UNIFORM, i, values[2], NULL, n);

      if(arraySize > 1)
      {
        isArray = true;

        size_t len = strlen(n);

        if(n[len - 3] == '[' && n[len - 2] == '0' && n[len - 1] == ']')
          n[len - 3] = 0;
      }
      else
      {
        arraySize = 1;
      }

      basename = n;

      // push it onto the list
      serialisedUniforms.ValueUniforms.push_back(ProgramUniform());
      ProgramUniform &uniform = serialisedUniforms.ValueUniforms.back();

      uniform.Basename = basename;
      uniform.IsArray = isArray;
      uniform.Values.resize(arraySize);

      // loop over every element in the array (arraySize = 1 for non arrays)
      for(GLint arr = 0; arr < arraySize; arr++)
      {
        ProgramUniformValue &uniformVal = uniform.Values[arr];
        uniformVal.Type = type;
        uniformVal.Location = srcLocation;

        std::string name = basename;

        // append the subscript if this item is an array.
        if(isArray)
        {
          name += StringFormat::Fmt("[%d]", arr);

          uniformVal.Location = srcLocation = gl.glGetUniformLocation(progSrc, name.c_str());
        }

        // fetch the data into the ProgramUniformValue, with the appropriate method for its type
        double *dv = uniformVal.data.dval;
        float *fv = uniformVal.data.fval;
        int32_t *iv = uniformVal.data.ival;
        uint32_t *uiv = uniformVal.data.uval;

        switch(type)
        {
          case eGL_FLOAT_MAT4:
          case eGL_FLOAT_MAT4x3:
          case eGL_FLOAT_MAT4x2:
          case eGL_FLOAT_MAT3:
          case eGL_FLOAT_MAT3x4:
          case eGL_FLOAT_MAT3x2:
          case eGL_FLOAT_MAT2:
          case eGL_FLOAT_MAT2x4:
          case eGL_FLOAT_MAT2x3:
          case eGL_FLOAT:
          case eGL_FLOAT_VEC2:
          case eGL_FLOAT_VEC3:
          case eGL_FLOAT_VEC4: gl.glGetUniformfv(progSrc, srcLocation, fv); break;
          case eGL_DOUBLE_MAT4:
          case eGL_DOUBLE_MAT4x3:
          case eGL_DOUBLE_MAT4x2:
          case eGL_DOUBLE_MAT3:
          case eGL_DOUBLE_MAT3x4:
          case eGL_DOUBLE_MAT3x2:
          case eGL_DOUBLE_MAT2:
          case eGL_DOUBLE_MAT2x4:
          case eGL_DOUBLE_MAT2x3:
          case eGL_DOUBLE:
          case eGL_DOUBLE_VEC2:
          case eGL_DOUBLE_VEC3:
          case eGL_DOUBLE_VEC4:
            gl.glGetUniformdv(progSrc, srcLocation, dv);
            break;

          // treat all samplers as just an int (since they just store their binding value)
          case eGL_SAMPLER_1D:
          case eGL_SAMPLER_2D:
          case eGL_SAMPLER_3D:
          case eGL_SAMPLER_CUBE:
          case eGL_SAMPLER_CUBE_MAP_ARRAY:
          case eGL_SAMPLER_1D_SHADOW:
          case eGL_SAMPLER_2D_SHADOW:
          case eGL_SAMPLER_1D_ARRAY:
          case eGL_SAMPLER_2D_ARRAY:
          case eGL_SAMPLER_1D_ARRAY_SHADOW:
          case eGL_SAMPLER_2D_ARRAY_SHADOW:
          case eGL_SAMPLER_2D_MULTISAMPLE:
          case eGL_SAMPLER_2D_MULTISAMPLE_ARRAY:
          case eGL_SAMPLER_CUBE_SHADOW:
          case eGL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
          case eGL_SAMPLER_BUFFER:
          case eGL_SAMPLER_2D_RECT:
          case eGL_SAMPLER_2D_RECT_SHADOW:
          case eGL_INT_SAMPLER_1D:
          case eGL_INT_SAMPLER_2D:
          case eGL_INT_SAMPLER_3D:
          case eGL_INT_SAMPLER_CUBE:
          case eGL_INT_SAMPLER_CUBE_MAP_ARRAY:
          case eGL_INT_SAMPLER_1D_ARRAY:
          case eGL_INT_SAMPLER_2D_ARRAY:
          case eGL_INT_SAMPLER_2D_MULTISAMPLE:
          case eGL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
          case eGL_INT_SAMPLER_BUFFER:
          case eGL_INT_SAMPLER_2D_RECT:
          case eGL_UNSIGNED_INT_SAMPLER_1D:
          case eGL_UNSIGNED_INT_SAMPLER_2D:
          case eGL_UNSIGNED_INT_SAMPLER_3D:
          case eGL_UNSIGNED_INT_SAMPLER_CUBE:
          case eGL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
          case eGL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
          case eGL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
          case eGL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
          case eGL_UNSIGNED_INT_SAMPLER_BUFFER:
          case eGL_UNSIGNED_INT_SAMPLER_2D_RECT:
          case eGL_IMAGE_1D:
          case eGL_IMAGE_2D:
          case eGL_IMAGE_3D:
          case eGL_IMAGE_2D_RECT:
          case eGL_IMAGE_CUBE:
          case eGL_IMAGE_BUFFER:
          case eGL_IMAGE_1D_ARRAY:
          case eGL_IMAGE_2D_ARRAY:
          case eGL_IMAGE_CUBE_MAP_ARRAY:
          case eGL_IMAGE_2D_MULTISAMPLE:
          case eGL_IMAGE_2D_MULTISAMPLE_ARRAY:
          case eGL_INT_IMAGE_1D:
          case eGL_INT_IMAGE_2D:
          case eGL_INT_IMAGE_3D:
          case eGL_INT_IMAGE_2D_RECT:
          case eGL_INT_IMAGE_CUBE:
          case eGL_INT_IMAGE_BUFFER:
          case eGL_INT_IMAGE_1D_ARRAY:
          case eGL_INT_IMAGE_2D_ARRAY:
          case eGL_INT_IMAGE_2D_MULTISAMPLE:
          case eGL_INT_IMAGE_2D_MULTISAMPLE_ARRAY:
          case eGL_UNSIGNED_INT_IMAGE_1D:
          case eGL_UNSIGNED_INT_IMAGE_2D:
          case eGL_UNSIGNED_INT_IMAGE_3D:
          case eGL_UNSIGNED_INT_IMAGE_2D_RECT:
          case eGL_UNSIGNED_INT_IMAGE_CUBE:
          case eGL_UNSIGNED_INT_IMAGE_BUFFER:
          case eGL_UNSIGNED_INT_IMAGE_1D_ARRAY:
          case eGL_UNSIGNED_INT_IMAGE_2D_ARRAY:
          case eGL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY:
          case eGL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE:
          case eGL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY:
          case eGL_UNSIGNED_INT_ATOMIC_COUNTER:
          case eGL_INT:
          case eGL_INT_VEC2:
          case eGL_INT_VEC3:
          case eGL_INT_VEC4:
            gl.glGetUniformiv(progSrc, srcLocation, iv);
            break;
          // bools are unsigned integers
          case eGL_UNSIGNED_INT:
          case eGL_BOOL:
          case eGL_UNSIGNED_INT_VEC2:
          case eGL_BOOL_VEC2:
          case eGL_UNSIGNED_INT_VEC3:
          case eGL_BOOL_VEC3:
          case eGL_UNSIGNED_INT_VEC4:
          case eGL_BOOL_VEC4: gl.glGetUniformuiv(progSrc, srcLocation, uiv); break;
          default: RDCERR("Unhandled uniform type '%s'", ToStr(type).c_str());
        }
      }
    }

    // now find how many UBOs we have, and store their binding indices
    GLint numUBOs = 0;
    gl.glGetProgramInterfaceiv(progSrc, eGL_UNIFORM_BLOCK, eGL_ACTIVE_RESOURCES, &numUBOs);

    serialisedUniforms.UBOBindings.reserve(numUBOs);

    for(GLint i = 0; i < numUBOs; i++)
    {
      GLenum prop = eGL_BUFFER_BINDING;
      uint32_t bind = 0;

      gl.glGetProgramResourceiv(progSrc, eGL_UNIFORM_BLOCK, i, 1, &prop, 1, NULL, (GLint *)&bind);

      char n[1024] = {0};
      gl.glGetProgramResourceName(progSrc, eGL_UNIFORM_BLOCK, i, 1023, NULL, n);

      serialisedUniforms.UBOBindings.push_back(ProgramBinding(n, bind));
    }

    // finally, if SSBOs are supported on this implementation, fetch their bindings
    GLint numSSBOs = 0;
    if(HasExt[ARB_shader_storage_buffer_object])
      gl.glGetProgramInterfaceiv(progSrc, eGL_SHADER_STORAGE_BLOCK, eGL_ACTIVE_RESOURCES, &numSSBOs);

    serialisedUniforms.SSBOBindings.reserve(numSSBOs);

    for(GLint i = 0; i < numSSBOs; i++)
    {
      GLenum prop = eGL_BUFFER_BINDING;
      uint32_t bind = 0;

      gl.glGetProgramResourceiv(progSrc, eGL_SHADER_STORAGE_BLOCK, i, 1, &prop, 1, NULL,
                                (GLint *)&bind);

      char n[1024] = {0};
      gl.glGetProgramResourceName(progSrc, eGL_SHADER_STORAGE_BLOCK, i, 1023, NULL, n);

      serialisedUniforms.SSBOBindings.push_back(ProgramBinding(n, bind));
    }
  }

  // now serialise all the bindings if we are serialising
  if(CheckConstParam(SerialiseUniforms) && ser)
  {
    ser->Serialise("ProgramUniforms", serialisedUniforms);
  }

  // if we are writing to a destination program and replaying, then apply the stored data from
  // serialisedUniforms
  if(CheckConstParam(WriteDestProgram) && IsReplayMode(state))
  {
    // loop over the loose global uniforms, see if there is an equivalent, and apply it.
    for(const ProgramUniform &uniform : serialisedUniforms.ValueUniforms)
    {
      for(size_t arr = 0; arr < uniform.Values.size(); arr++)
      {
        const ProgramUniformValue &val = uniform.Values[arr];

        std::string name = uniform.Basename;

        if(uniform.IsArray)
          name += StringFormat::Fmt("[%u]", (uint32_t)arr);

        GLint dstLocation = gl.glGetUniformLocation(progDst, name.c_str());
        if(locTranslate)
          (*locTranslate)[val.Location] = dstLocation;

        // don't try and apply the uniform if the new location is -1
        if(dstLocation == -1)
          continue;

        const double *dv = val.data.dval;
        const float *fv = val.data.fval;
        const int32_t *iv = val.data.ival;
        const uint32_t *uiv = val.data.uval;

        // call the appropriate function to apply the data to the destination program
        switch(val.Type)
        {
          case eGL_FLOAT_MAT4:
            gl.glProgramUniformMatrix4fv(progDst, dstLocation, 1, false, fv);
            break;
          case eGL_FLOAT_MAT4x3:
            gl.glProgramUniformMatrix4x3fv(progDst, dstLocation, 1, false, fv);
            break;
          case eGL_FLOAT_MAT4x2:
            gl.glProgramUniformMatrix4x2fv(progDst, dstLocation, 1, false, fv);
            break;
          case eGL_FLOAT_MAT3:
            gl.glProgramUniformMatrix3fv(progDst, dstLocation, 1, false, fv);
            break;
          case eGL_FLOAT_MAT3x4:
            gl.glProgramUniformMatrix3x4fv(progDst, dstLocation, 1, false, fv);
            break;
          case eGL_FLOAT_MAT3x2:
            gl.glProgramUniformMatrix3x2fv(progDst, dstLocation, 1, false, fv);
            break;
          case eGL_FLOAT_MAT2:
            gl.glProgramUniformMatrix2fv(progDst, dstLocation, 1, false, fv);
            break;
          case eGL_FLOAT_MAT2x4:
            gl.glProgramUniformMatrix2x4fv(progDst, dstLocation, 1, false, fv);
            break;
          case eGL_FLOAT_MAT2x3:
            gl.glProgramUniformMatrix2x3fv(progDst, dstLocation, 1, false, fv);
            break;
          case eGL_DOUBLE_MAT4:
            gl.glProgramUniformMatrix4dv(progDst, dstLocation, 1, false, dv);
            break;
          case eGL_DOUBLE_MAT4x3:
            gl.glProgramUniformMatrix4x3dv(progDst, dstLocation, 1, false, dv);
            break;
          case eGL_DOUBLE_MAT4x2:
            gl.glProgramUniformMatrix4x2dv(progDst, dstLocation, 1, false, dv);
            break;
          case eGL_DOUBLE_MAT3:
            gl.glProgramUniformMatrix3dv(progDst, dstLocation, 1, false, dv);
            break;
          case eGL_DOUBLE_MAT3x4:
            gl.glProgramUniformMatrix3x4dv(progDst, dstLocation, 1, false, dv);
            break;
          case eGL_DOUBLE_MAT3x2:
            gl.glProgramUniformMatrix3x2dv(progDst, dstLocation, 1, false, dv);
            break;
          case eGL_DOUBLE_MAT2:
            gl.glProgramUniformMatrix2dv(progDst, dstLocation, 1, false, dv);
            break;
          case eGL_DOUBLE_MAT2x4:
            gl.glProgramUniformMatrix2x4dv(progDst, dstLocation, 1, false, dv);
            break;
          case eGL_DOUBLE_MAT2x3:
            gl.glProgramUniformMatrix2x3dv(progDst, dstLocation, 1, false, dv);
            break;
          case eGL_FLOAT: gl.glProgramUniform1fv(progDst, dstLocation, 1, fv); break;
          case eGL_FLOAT_VEC2: gl.glProgramUniform2fv(progDst, dstLocation, 1, fv); break;
          case eGL_FLOAT_VEC3: gl.glProgramUniform3fv(progDst, dstLocation, 1, fv); break;
          case eGL_FLOAT_VEC4: gl.glProgramUniform4fv(progDst, dstLocation, 1, fv); break;
          case eGL_DOUBLE: gl.glProgramUniform1dv(progDst, dstLocation, 1, dv); break;
          case eGL_DOUBLE_VEC2: gl.glProgramUniform2dv(progDst, dstLocation, 1, dv); break;
          case eGL_DOUBLE_VEC3: gl.glProgramUniform3dv(progDst, dstLocation, 1, dv); break;
          case eGL_DOUBLE_VEC4: gl.glProgramUniform4dv(progDst, dstLocation, 1, dv); break;

          case eGL_IMAGE_1D:
          case eGL_IMAGE_2D:
          case eGL_IMAGE_3D:
          case eGL_IMAGE_2D_RECT:
          case eGL_IMAGE_CUBE:
          case eGL_IMAGE_BUFFER:
          case eGL_IMAGE_1D_ARRAY:
          case eGL_IMAGE_2D_ARRAY:
          case eGL_IMAGE_CUBE_MAP_ARRAY:
          case eGL_IMAGE_2D_MULTISAMPLE:
          case eGL_IMAGE_2D_MULTISAMPLE_ARRAY:
          case eGL_INT_IMAGE_1D:
          case eGL_INT_IMAGE_2D:
          case eGL_INT_IMAGE_3D:
          case eGL_INT_IMAGE_2D_RECT:
          case eGL_INT_IMAGE_CUBE:
          case eGL_INT_IMAGE_BUFFER:
          case eGL_INT_IMAGE_1D_ARRAY:
          case eGL_INT_IMAGE_2D_ARRAY:
          case eGL_INT_IMAGE_2D_MULTISAMPLE:
          case eGL_INT_IMAGE_2D_MULTISAMPLE_ARRAY:
          case eGL_UNSIGNED_INT_IMAGE_1D:
          case eGL_UNSIGNED_INT_IMAGE_2D:
          case eGL_UNSIGNED_INT_IMAGE_3D:
          case eGL_UNSIGNED_INT_IMAGE_2D_RECT:
          case eGL_UNSIGNED_INT_IMAGE_CUBE:
          case eGL_UNSIGNED_INT_IMAGE_BUFFER:
          case eGL_UNSIGNED_INT_IMAGE_1D_ARRAY:
          case eGL_UNSIGNED_INT_IMAGE_2D_ARRAY:
          case eGL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY:
          case eGL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE:
          case eGL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY:
          case eGL_UNSIGNED_INT_ATOMIC_COUNTER:
            if(IsGLES)
              // Image uniforms cannot be re-assigned in GLES.
              break;
          // deliberate fall-through
          // treat all samplers as just an int (since they just store their binding value)
          case eGL_SAMPLER_1D:
          case eGL_SAMPLER_2D:
          case eGL_SAMPLER_3D:
          case eGL_SAMPLER_CUBE:
          case eGL_SAMPLER_CUBE_MAP_ARRAY:
          case eGL_SAMPLER_1D_SHADOW:
          case eGL_SAMPLER_2D_SHADOW:
          case eGL_SAMPLER_1D_ARRAY:
          case eGL_SAMPLER_2D_ARRAY:
          case eGL_SAMPLER_1D_ARRAY_SHADOW:
          case eGL_SAMPLER_2D_ARRAY_SHADOW:
          case eGL_SAMPLER_2D_MULTISAMPLE:
          case eGL_SAMPLER_2D_MULTISAMPLE_ARRAY:
          case eGL_SAMPLER_CUBE_SHADOW:
          case eGL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
          case eGL_SAMPLER_BUFFER:
          case eGL_SAMPLER_2D_RECT:
          case eGL_SAMPLER_2D_RECT_SHADOW:
          case eGL_INT_SAMPLER_1D:
          case eGL_INT_SAMPLER_2D:
          case eGL_INT_SAMPLER_3D:
          case eGL_INT_SAMPLER_CUBE:
          case eGL_INT_SAMPLER_CUBE_MAP_ARRAY:
          case eGL_INT_SAMPLER_1D_ARRAY:
          case eGL_INT_SAMPLER_2D_ARRAY:
          case eGL_INT_SAMPLER_2D_MULTISAMPLE:
          case eGL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
          case eGL_INT_SAMPLER_BUFFER:
          case eGL_INT_SAMPLER_2D_RECT:
          case eGL_UNSIGNED_INT_SAMPLER_1D:
          case eGL_UNSIGNED_INT_SAMPLER_2D:
          case eGL_UNSIGNED_INT_SAMPLER_3D:
          case eGL_UNSIGNED_INT_SAMPLER_CUBE:
          case eGL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
          case eGL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
          case eGL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
          case eGL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
          case eGL_UNSIGNED_INT_SAMPLER_BUFFER:
          case eGL_UNSIGNED_INT_SAMPLER_2D_RECT:
          case eGL_INT: gl.glProgramUniform1iv(progDst, dstLocation, 1, iv); break;
          case eGL_INT_VEC2: gl.glProgramUniform2iv(progDst, dstLocation, 1, iv); break;
          case eGL_INT_VEC3: gl.glProgramUniform3iv(progDst, dstLocation, 1, iv); break;
          case eGL_INT_VEC4: gl.glProgramUniform4iv(progDst, dstLocation, 1, iv); break;
          case eGL_UNSIGNED_INT:
          case eGL_BOOL: gl.glProgramUniform1uiv(progDst, dstLocation, 1, uiv); break;
          case eGL_UNSIGNED_INT_VEC2:
          case eGL_BOOL_VEC2: gl.glProgramUniform2uiv(progDst, dstLocation, 1, uiv); break;
          case eGL_UNSIGNED_INT_VEC3:
          case eGL_BOOL_VEC3: gl.glProgramUniform3uiv(progDst, dstLocation, 1, uiv); break;
          case eGL_UNSIGNED_INT_VEC4:
          case eGL_BOOL_VEC4: gl.glProgramUniform4uiv(progDst, dstLocation, 1, uiv); break;
          default: RDCERR("Unhandled uniform type '%s'", ToStr(val.Type).c_str());
        }
      }
    }

    // apply UBO bindings
    for(const ProgramBinding &bind : serialisedUniforms.UBOBindings)
    {
      GLuint idx = gl.glGetUniformBlockIndex(progDst, bind.Name.c_str());
      if(idx != GL_INVALID_INDEX)
        gl.glUniformBlockBinding(progDst, idx, bind.Binding);
    }

    // apply SSBO bindings
    for(const ProgramBinding &bind : serialisedUniforms.SSBOBindings)
    {
      GLuint idx = gl.glGetProgramResourceIndex(progDst, eGL_SHADER_STORAGE_BLOCK, bind.Name.c_str());
      if(idx != GL_INVALID_INDEX)
      {
        if(gl.glShaderStorageBlockBinding)
        {
          gl.glShaderStorageBlockBinding(progDst, idx, bind.Binding);
        }
        else
        {
          // TODO glShaderStorageBlockBinding is not core GLES
          RDCERR("glShaderStorageBlockBinding is not supported!");
        }
      }
    }
  }
}
void WrappedVulkan::vkUpdateDescriptorSets(
    VkDevice                                    device,
    uint32_t                                    writeCount,
    const VkWriteDescriptorSet*                 pDescriptorWrites,
    uint32_t                                    copyCount,
    const VkCopyDescriptorSet*                  pDescriptorCopies)
{
    {
        // need to count up number of descriptor infos, to be able to alloc enough space
        uint32_t numInfos = 0;
        for(uint32_t i=0; i < writeCount; i++) numInfos += pDescriptorWrites[i].descriptorCount;

        byte *memory = GetTempMemory(sizeof(VkDescriptorBufferInfo)*numInfos +
                                     sizeof(VkWriteDescriptorSet)*writeCount + sizeof(VkCopyDescriptorSet)*copyCount);

        RDCCOMPILE_ASSERT(sizeof(VkDescriptorBufferInfo) >= sizeof(VkDescriptorImageInfo), "Descriptor structs sizes are unexpected, ensure largest size is used");

        VkWriteDescriptorSet *unwrappedWrites = (VkWriteDescriptorSet *)memory;
        VkCopyDescriptorSet *unwrappedCopies = (VkCopyDescriptorSet *)(unwrappedWrites + writeCount);
        VkDescriptorBufferInfo *nextDescriptors = (VkDescriptorBufferInfo *)(unwrappedCopies + copyCount);

        for(uint32_t i=0; i < writeCount; i++)
        {
            unwrappedWrites[i] = pDescriptorWrites[i];
            unwrappedWrites[i].dstSet = Unwrap(unwrappedWrites[i].dstSet);

            VkDescriptorBufferInfo *bufInfos = nextDescriptors;
            VkDescriptorImageInfo *imInfos = (VkDescriptorImageInfo *)bufInfos;
            VkBufferView *bufViews = (VkBufferView *)bufInfos;
            nextDescriptors += pDescriptorWrites[i].descriptorCount;

            // unwrap and assign the appropriate array
            if(pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER ||
                    pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER)
            {
                unwrappedWrites[i].pTexelBufferView = (VkBufferView *)bufInfos;
                for(uint32_t j=0; j < pDescriptorWrites[i].descriptorCount; j++)
                    bufViews[j] = Unwrap(pDescriptorWrites[i].pTexelBufferView[j]);
            }
            else if(pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER ||
                    pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ||
                    pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE ||
                    pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE ||
                    pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT)
            {
                unwrappedWrites[i].pImageInfo = (VkDescriptorImageInfo *)bufInfos;
                for(uint32_t j=0; j < pDescriptorWrites[i].descriptorCount; j++)
                {
                    imInfos[j].imageView = Unwrap(pDescriptorWrites[i].pImageInfo[j].imageView);
                    imInfos[j].sampler = Unwrap(pDescriptorWrites[i].pImageInfo[j].sampler);
                    imInfos[j].imageLayout = pDescriptorWrites[i].pImageInfo[j].imageLayout;
                }
            }
            else
            {
                unwrappedWrites[i].pBufferInfo = bufInfos;
                for(uint32_t j=0; j < pDescriptorWrites[i].descriptorCount; j++)
                {
                    bufInfos[j].buffer = Unwrap(pDescriptorWrites[i].pBufferInfo[j].buffer);
                    bufInfos[j].offset = pDescriptorWrites[i].pBufferInfo[j].offset;
                    bufInfos[j].range = pDescriptorWrites[i].pBufferInfo[j].range;
                }
            }
        }

        for(uint32_t i=0; i < copyCount; i++)
        {
            unwrappedCopies[i] = pDescriptorCopies[i];
            unwrappedCopies[i].dstSet = Unwrap(unwrappedCopies[i].dstSet);
            unwrappedCopies[i].srcSet = Unwrap(unwrappedCopies[i].srcSet);
        }

        ObjDisp(device)->UpdateDescriptorSets(Unwrap(device), writeCount, unwrappedWrites, copyCount, unwrappedCopies);
    }

    bool capframe = false;
    {
        SCOPED_LOCK(m_CapTransitionLock);
        capframe = (m_State == WRITING_CAPFRAME);
    }

    if(capframe)
    {
        // don't have to mark referenced any of the resources pointed to by the descriptor set - that's handled
        // on queue submission by marking ref'd all the current bindings of the sets referenced by the cmd buffer

        for(uint32_t i=0; i < writeCount; i++)
        {
            {
                CACHE_THREAD_SERIALISER();

                SCOPED_SERIALISE_CONTEXT(UPDATE_DESC_SET);
                Serialise_vkUpdateDescriptorSets(localSerialiser, device, 1, &pDescriptorWrites[i], 0, NULL);

                m_FrameCaptureRecord->AddChunk(scope.Get());
            }

            // as long as descriptor sets are forced to have initial states, we don't have to mark them ref'd for
            // write here. The reason being that as long as we only mark them as ref'd when they're actually bound,
            // we can safely skip the ref here and it means any descriptor set updates of descriptor sets that are
            // never used in the frame can be ignored.
            //GetResourceManager()->MarkResourceFrameReferenced(GetResID(pDescriptorWrites[i].destSet), eFrameRef_Write);
        }

        for(uint32_t i=0; i < copyCount; i++)
        {
            {
                CACHE_THREAD_SERIALISER();

                SCOPED_SERIALISE_CONTEXT(UPDATE_DESC_SET);
                Serialise_vkUpdateDescriptorSets(localSerialiser, device, 0, NULL, 1, &pDescriptorCopies[i]);

                m_FrameCaptureRecord->AddChunk(scope.Get());
            }

            // Like writes we don't have to mark the written descriptor set as used because unless it's bound somewhere
            // we don't need it anyway. However we DO have to mark the source set as used because it doesn't have to
            // be bound to still be needed (think about if the dest set is bound somewhere after this copy - what refs
            // the source set?).
            // At the same time as ref'ing the source set, we must ref all of its resources (via the bindFrameRefs).
            // We just ref all rather than looking at only the copied sets to keep things simple.
            // This does mean a slightly conservative ref'ing if the dest set doesn't end up getting bound, but we only
            // do this during frame capture so it's not too bad.
            //GetResourceManager()->MarkResourceFrameReferenced(GetResID(pDescriptorCopies[i].destSet), eFrameRef_Write);

            {
                GetResourceManager()->MarkResourceFrameReferenced(GetResID(pDescriptorCopies[i].srcSet), eFrameRef_Read);

                VkResourceRecord *setrecord = GetRecord(pDescriptorCopies[i].srcSet);

                for(auto refit = setrecord->descInfo->bindFrameRefs.begin(); refit != setrecord->descInfo->bindFrameRefs.end(); ++refit)
                {
                    GetResourceManager()->MarkResourceFrameReferenced(refit->first, refit->second.second);

                    if(refit->second.first & DescriptorSetData::SPARSE_REF_BIT)
                    {
                        VkResourceRecord *record = GetResourceManager()->GetResourceRecord(refit->first);

                        GetResourceManager()->MarkSparseMapReferenced(record->sparseInfo);
                    }
                }
            }
        }
    }

    // need to track descriptor set contents whether capframing or idle
    if(m_State >= WRITING)
    {
        for(uint32_t i=0; i < writeCount; i++)
        {
            VkResourceRecord *record = GetRecord(pDescriptorWrites[i].dstSet);
            RDCASSERT(record->descInfo && record->descInfo->layout);
            const DescSetLayout &layout = *record->descInfo->layout;

            RDCASSERT(pDescriptorWrites[i].dstBinding < record->descInfo->descBindings.size());

            DescriptorSetSlot *binding = record->descInfo->descBindings[pDescriptorWrites[i].dstBinding];

            FrameRefType ref = eFrameRef_Write;

            switch(layout.bindings[pDescriptorWrites[i].dstBinding].descriptorType)
            {
            case VK_DESCRIPTOR_TYPE_SAMPLER:
            case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
            case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
            case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
            case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
            case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
            case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
                ref = eFrameRef_Read;
                break;
            case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
            case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
            case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
            case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
                ref = eFrameRef_Write;
                break;
            default:
                RDCERR("Unexpected descriptor type");
            }

            // We need to handle the cases where these bindings are stale:
            // ie. image handle 0xf00baa is allocated
            // bound into a descriptor set
            // image is released
            // descriptor set is bound but this image is never used by shader etc.
            //
            // worst case, a new image or something has been added with this handle -
            // in this case we end up ref'ing an image that isn't actually used.
            // Worst worst case, we ref an image as write when actually it's not, but
            // this is likewise not a serious problem, and rather difficult to solve
            // (would need to version handles somehow, but don't have enough bits
            // to do that reliably).
            //
            // This is handled by RemoveBindFrameRef silently dropping id == ResourceId()

            for(uint32_t d=0; d < pDescriptorWrites[i].descriptorCount; d++)
            {
                DescriptorSetSlot &bind = binding[pDescriptorWrites[i].dstArrayElement + d];

                if(bind.texelBufferView != VK_NULL_HANDLE)
                {
                    record->RemoveBindFrameRef(GetResID(bind.texelBufferView));
                    if(GetRecord(bind.texelBufferView)->baseResource != ResourceId())
                        record->RemoveBindFrameRef(GetRecord(bind.texelBufferView)->baseResource);
                }
                if(bind.imageInfo.imageView != VK_NULL_HANDLE)
                {
                    record->RemoveBindFrameRef(GetResID(bind.imageInfo.imageView));
                    record->RemoveBindFrameRef(GetRecord(bind.imageInfo.imageView)->baseResource);
                    if(GetRecord(bind.imageInfo.imageView)->baseResourceMem != ResourceId())
                        record->RemoveBindFrameRef(GetRecord(bind.imageInfo.imageView)->baseResourceMem);
                }
                if(bind.imageInfo.sampler != VK_NULL_HANDLE)
                {
                    record->RemoveBindFrameRef(GetResID(bind.imageInfo.sampler));
                }
                if(bind.bufferInfo.buffer != VK_NULL_HANDLE)
                {
                    record->RemoveBindFrameRef(GetResID(bind.bufferInfo.buffer));
                    if(GetRecord(bind.bufferInfo.buffer)->baseResource != ResourceId())
                        record->RemoveBindFrameRef(GetRecord(bind.bufferInfo.buffer)->baseResource);
                }

                // NULL everything out now so that we don't accidentally reference an object
                // that was removed already
                bind.texelBufferView = VK_NULL_HANDLE;
                bind.bufferInfo.buffer = VK_NULL_HANDLE;
                bind.imageInfo.imageView = VK_NULL_HANDLE;
                bind.imageInfo.sampler = VK_NULL_HANDLE;

                if(pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER ||
                        pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER)
                {
                    bind.texelBufferView = pDescriptorWrites[i].pTexelBufferView[d];
                }
                else if(pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER ||
                        pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ||
                        pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE ||
                        pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE ||
                        pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT)
                {
                    bind.imageInfo = pDescriptorWrites[i].pImageInfo[d];

                    // ignore descriptors not part of the write, by NULL'ing out those members
                    // as they might not even point to a valid object
                    if(pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER)
                        bind.imageInfo.imageView = VK_NULL_HANDLE;
                    else if(pDescriptorWrites[i].descriptorType != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
                        bind.imageInfo.sampler = VK_NULL_HANDLE;
                }
                else
                {
                    bind.bufferInfo = pDescriptorWrites[i].pBufferInfo[d];
                }

                if(bind.texelBufferView != VK_NULL_HANDLE)
                {
                    record->AddBindFrameRef(GetResID(bind.texelBufferView), eFrameRef_Read, GetRecord(bind.texelBufferView)->sparseInfo != NULL);
                    if(GetRecord(bind.texelBufferView)->baseResource != ResourceId())
                        record->AddBindFrameRef(GetRecord(bind.texelBufferView)->baseResource, ref);
                }
                if(bind.imageInfo.imageView != VK_NULL_HANDLE)
                {
                    record->AddBindFrameRef(GetResID(bind.imageInfo.imageView), eFrameRef_Read, GetRecord(bind.imageInfo.imageView)->sparseInfo != NULL);
                    record->AddBindFrameRef(GetRecord(bind.imageInfo.imageView)->baseResource, ref);
                    if(GetRecord(bind.imageInfo.imageView)->baseResourceMem != ResourceId())
                        record->AddBindFrameRef(GetRecord(bind.imageInfo.imageView)->baseResourceMem, eFrameRef_Read);
                }
                if(bind.imageInfo.sampler != VK_NULL_HANDLE)
                {
                    record->AddBindFrameRef(GetResID(bind.imageInfo.sampler), eFrameRef_Read);
                }
                if(bind.bufferInfo.buffer != VK_NULL_HANDLE)
                {
                    record->AddBindFrameRef(GetResID(bind.bufferInfo.buffer), eFrameRef_Read, GetRecord(bind.bufferInfo.buffer)->sparseInfo != NULL);
                    if(GetRecord(bind.bufferInfo.buffer)->baseResource != ResourceId())
                        record->AddBindFrameRef(GetRecord(bind.bufferInfo.buffer)->baseResource, ref);
                }
            }
        }

        // this is almost identical to the above loop, except that instead of sourcing the descriptors
        // from the writedescriptor struct, we source it from our stored bindings on the source
        // descrpitor set

        for(uint32_t i=0; i < copyCount; i++)
        {
            VkResourceRecord *dstrecord = GetRecord(pDescriptorCopies[i].dstSet);
            RDCASSERT(dstrecord->descInfo && dstrecord->descInfo->layout);
            const DescSetLayout &layout = *dstrecord->descInfo->layout;

            VkResourceRecord *srcrecord = GetRecord(pDescriptorCopies[i].srcSet);

            RDCASSERT(pDescriptorCopies[i].dstBinding < dstrecord->descInfo->descBindings.size());
            RDCASSERT(pDescriptorCopies[i].srcBinding < srcrecord->descInfo->descBindings.size());

            DescriptorSetSlot *dstbinding = dstrecord->descInfo->descBindings[pDescriptorCopies[i].dstBinding];
            DescriptorSetSlot *srcbinding = srcrecord->descInfo->descBindings[pDescriptorCopies[i].srcBinding];

            FrameRefType ref = eFrameRef_Write;

            switch(layout.bindings[pDescriptorCopies[i].dstBinding].descriptorType)
            {
            case VK_DESCRIPTOR_TYPE_SAMPLER:
            case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
            case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
            case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
            case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
            case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
            case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
                ref = eFrameRef_Read;
                break;
            case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
            case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
            case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
            case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
                ref = eFrameRef_Write;
                break;
            default:
                RDCERR("Unexpected descriptor type");
            }

            for(uint32_t d=0; d < pDescriptorCopies[i].descriptorCount; d++)
            {
                DescriptorSetSlot &bind = dstbinding[pDescriptorCopies[i].dstArrayElement + d];

                if(bind.texelBufferView != VK_NULL_HANDLE)
                {
                    dstrecord->RemoveBindFrameRef(GetResID(bind.texelBufferView));
                    if(GetRecord(bind.texelBufferView)->baseResource != ResourceId())
                        dstrecord->RemoveBindFrameRef(GetRecord(bind.texelBufferView)->baseResource);
                }
                if(bind.imageInfo.imageView != VK_NULL_HANDLE)
                {
                    dstrecord->RemoveBindFrameRef(GetResID(bind.imageInfo.imageView));
                    dstrecord->RemoveBindFrameRef(GetRecord(bind.imageInfo.imageView)->baseResource);
                    if(GetRecord(bind.imageInfo.imageView)->baseResourceMem != ResourceId())
                        dstrecord->RemoveBindFrameRef(GetRecord(bind.imageInfo.imageView)->baseResourceMem);
                }
                if(bind.imageInfo.sampler != VK_NULL_HANDLE)
                {
                    dstrecord->RemoveBindFrameRef(GetResID(bind.imageInfo.sampler));
                }
                if(bind.bufferInfo.buffer != VK_NULL_HANDLE)
                {
                    dstrecord->RemoveBindFrameRef(GetResID(bind.bufferInfo.buffer));
                    if(GetRecord(bind.bufferInfo.buffer)->baseResource != ResourceId())
                        dstrecord->RemoveBindFrameRef(GetRecord(bind.bufferInfo.buffer)->baseResource);
                }

                bind = srcbinding[pDescriptorCopies[i].srcArrayElement + d];

                if(bind.texelBufferView != VK_NULL_HANDLE)
                {
                    dstrecord->AddBindFrameRef(GetResID(bind.texelBufferView), eFrameRef_Read, GetRecord(bind.texelBufferView)->sparseInfo != NULL);
                    if(GetRecord(bind.texelBufferView)->baseResource != ResourceId())
                        dstrecord->AddBindFrameRef(GetRecord(bind.texelBufferView)->baseResource, ref);
                }
                if(bind.imageInfo.imageView != VK_NULL_HANDLE)
                {
                    dstrecord->AddBindFrameRef(GetResID(bind.imageInfo.imageView), eFrameRef_Read, GetRecord(bind.imageInfo.imageView)->sparseInfo != NULL);
                    dstrecord->AddBindFrameRef(GetRecord(bind.imageInfo.imageView)->baseResource, ref);
                    if(GetRecord(bind.imageInfo.imageView)->baseResourceMem != ResourceId())
                        dstrecord->AddBindFrameRef(GetRecord(bind.imageInfo.imageView)->baseResourceMem, eFrameRef_Read);
                }
                if(bind.imageInfo.sampler != VK_NULL_HANDLE)
                {
                    dstrecord->AddBindFrameRef(GetResID(bind.imageInfo.sampler), ref);
                }
                if(bind.bufferInfo.buffer != VK_NULL_HANDLE)
                {
                    dstrecord->AddBindFrameRef(GetResID(bind.bufferInfo.buffer), eFrameRef_Read, GetRecord(bind.bufferInfo.buffer)->sparseInfo != NULL);
                    if(GetRecord(bind.bufferInfo.buffer)->baseResource != ResourceId())
                        dstrecord->AddBindFrameRef(GetRecord(bind.bufferInfo.buffer)->baseResource, ref);
                }
            }
        }

    }
}
Example #10
0
string CompileSPIRV(SPIRVShaderStage shadType, const std::vector<std::string> &sources, vector<uint32_t> &spirv)
{
	if(shadType >= eSPIRVInvalid)
		return "Invalid shader stage specified";

	string errors = "";

	const char **strs = new const char *[sources.size()];

	for(size_t i=0; i < sources.size(); i++)
		strs[i] = sources[i].c_str();

	RDCCOMPILE_ASSERT(
		(int)EShLangVertex == (int)eSPIRVVertex &&
		(int)EShLangTessControl == (int)eSPIRVTessControl &&
		(int)EShLangTessEvaluation == (int)eSPIRVTessEvaluation &&
		(int)EShLangGeometry == (int)eSPIRVGeometry &&
		(int)EShLangCompute == (int)eSPIRVCompute,
		"Shader language enums don't match");

	{
		EShLanguage lang = EShLanguage(shadType);

		glslang::TShader *shader = new glslang::TShader(lang);

		shader->setStrings(strs, (int)sources.size());

		bool success = shader->parse(&DefaultResources, 110, false, EShMessages(EShMsgSpvRules|EShMsgVulkanRules));

		if(!success)
		{
			errors = "Shader failed to compile:\n\n";
			errors += shader->getInfoLog();
			errors += "\n\n";
			errors += shader->getInfoDebugLog();
		}
		else
		{
			glslang::TProgram *program = new glslang::TProgram();

			program->addShader(shader);

			success = program->link(EShMsgDefault);

			if(!success)
			{
				errors = "Program failed to link:\n\n";
				errors += program->getInfoLog();
				errors += "\n\n";
				errors += program->getInfoDebugLog();
			}
			else
			{
				glslang::TIntermediate *intermediate = program->getIntermediate(lang);

				// if we successfully compiled and linked, we must have the stage we started with
				RDCASSERT(intermediate);

				glslang::GlslangToSpv(*intermediate, spirv);
			}

			delete program;
		}

		delete shader;
	}

	delete[] strs;

	return errors;
}
Example #11
0
void GLRenderState::FetchState(void *ctx, WrappedOpenGL *gl)
{
	GLint boolread = 0;
	// TODO check GL_MAX_*
	// TODO check the extensions/core version for these is around
	
	{
		GLenum pnames[] =
		{
			eGL_CLIP_DISTANCE0,
			eGL_CLIP_DISTANCE1,
			eGL_CLIP_DISTANCE2,
			eGL_CLIP_DISTANCE3,
			eGL_CLIP_DISTANCE4,
			eGL_CLIP_DISTANCE5,
			eGL_CLIP_DISTANCE6,
			eGL_CLIP_DISTANCE7,
			eGL_COLOR_LOGIC_OP,
			eGL_CULL_FACE,
			eGL_DEPTH_CLAMP,
			eGL_DEPTH_TEST,
			eGL_DEPTH_BOUNDS_TEST_EXT,
			eGL_DITHER,
			eGL_FRAMEBUFFER_SRGB,
			eGL_LINE_SMOOTH,
			eGL_MULTISAMPLE,
			eGL_POLYGON_SMOOTH,
			eGL_POLYGON_OFFSET_FILL,
			eGL_POLYGON_OFFSET_LINE,
			eGL_POLYGON_OFFSET_POINT,
			eGL_PROGRAM_POINT_SIZE,
			eGL_PRIMITIVE_RESTART,
			eGL_PRIMITIVE_RESTART_FIXED_INDEX,
			eGL_SAMPLE_ALPHA_TO_COVERAGE,
			eGL_SAMPLE_ALPHA_TO_ONE,
			eGL_SAMPLE_COVERAGE,
			eGL_SAMPLE_MASK,
			eGL_RASTER_MULTISAMPLE_EXT,
			eGL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT,
			eGL_STENCIL_TEST,
			eGL_TEXTURE_CUBE_MAP_SEAMLESS,
			eGL_BLEND_ADVANCED_COHERENT_KHR,
		};

		RDCCOMPILE_ASSERT(ARRAY_COUNT(pnames) == eEnabled_Count, "Wrong number of pnames");
		
		for(GLuint i=0; i < eEnabled_Count; i++)
		{
			if(pnames[i] == eGL_BLEND_ADVANCED_COHERENT_KHR && !ExtensionSupported[ExtensionSupported_KHR_blend_equation_advanced_coherent])
			{
				Enabled[i] = true;
				continue;
			}
			
			if((pnames[i] == eGL_RASTER_MULTISAMPLE_EXT || pnames[i] == eGL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT)
				 && !ExtensionSupported[ExtensionSupported_EXT_raster_multisample])
			{
				Enabled[i] = false;
				continue;
			}

			Enabled[i] = (m_Real->glIsEnabled(pnames[i]) == GL_TRUE);
		}
	}

	m_Real->glGetIntegerv(eGL_ACTIVE_TEXTURE, (GLint *)&ActiveTexture);
	
	// TODO fetch bindings for other types than 2D
	for(GLuint i=0; i < (GLuint)ARRAY_COUNT(Tex2D); i++)
	{
		m_Real->glActiveTexture(GLenum(eGL_TEXTURE0 + i));
		m_Real->glGetIntegerv(eGL_TEXTURE_BINDING_2D, (GLint*)&Tex2D[i]);
		m_Real->glGetIntegerv(eGL_SAMPLER_BINDING, (GLint*)&Samplers[i]);
	}
	
	m_Real->glActiveTexture(ActiveTexture);
	
	m_Real->glGetIntegerv(eGL_VERTEX_ARRAY_BINDING, (GLint *)&VAO);
	m_Real->glGetIntegerv(eGL_TRANSFORM_FEEDBACK_BINDING, (GLint *)&FeedbackObj);
	
	// the spec says that you can only query for the format that was previously set, or you get
	// undefined results. Ie. if someone set ints, this might return anything. However there's also
	// no way to query for the type so we just have to hope for the best and hope most people are
	// sane and don't use these except for a default "all 0s" attrib.

	GLuint maxNumAttribs = 0;
	m_Real->glGetIntegerv(eGL_MAX_VERTEX_ATTRIBS, (GLint *)&maxNumAttribs);
	for(GLuint i=0; i < RDCMIN(maxNumAttribs, (GLuint)ARRAY_COUNT(GenericVertexAttribs)); i++)
		m_Real->glGetVertexAttribfv(i, eGL_CURRENT_VERTEX_ATTRIB, &GenericVertexAttribs[i].x);
	
	m_Real->glGetFloatv(eGL_POINT_FADE_THRESHOLD_SIZE, &PointFadeThresholdSize);
	m_Real->glGetIntegerv(eGL_POINT_SPRITE_COORD_ORIGIN, (GLint*)&PointSpriteOrigin);
	m_Real->glGetFloatv(eGL_LINE_WIDTH, &LineWidth);
	m_Real->glGetFloatv(eGL_POINT_SIZE, &PointSize);
	
	m_Real->glGetIntegerv(eGL_PRIMITIVE_RESTART_INDEX, (GLint *)&PrimitiveRestartIndex);
	if(GLCoreVersion >= 45 || ExtensionSupported[ExtensionSupported_ARB_clip_control])
	{
		m_Real->glGetIntegerv(eGL_CLIP_ORIGIN, (GLint *)&ClipOrigin);
		m_Real->glGetIntegerv(eGL_CLIP_DEPTH_MODE, (GLint *)&ClipDepth);
	}
	else
	{
		ClipOrigin = eGL_LOWER_LEFT;
		ClipDepth = eGL_NEGATIVE_ONE_TO_ONE;
	}
	m_Real->glGetIntegerv(eGL_PROVOKING_VERTEX, (GLint *)&ProvokingVertex);

	m_Real->glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint *)&Program);
	m_Real->glGetIntegerv(eGL_PROGRAM_PIPELINE_BINDING, (GLint *)&Pipeline);
	
	const GLenum shs[] = {
		eGL_VERTEX_SHADER,
		eGL_TESS_CONTROL_SHADER,
		eGL_TESS_EVALUATION_SHADER,
		eGL_GEOMETRY_SHADER,
		eGL_FRAGMENT_SHADER,
		VendorCheck[VendorCheck_AMD_pipeline_compute_query] ? eGL_NONE : eGL_COMPUTE_SHADER,
	};

	RDCCOMPILE_ASSERT(ARRAY_COUNT(shs) == ARRAY_COUNT(Subroutines), "Subroutine array not the right size");
	for(size_t s=0; s < ARRAY_COUNT(shs); s++)
	{
		GLuint prog = Program;
		if(prog == 0 && Pipeline != 0 && shs[s] != eGL_NONE)
			m_Real->glGetProgramPipelineiv(Pipeline, shs[s], (GLint *)&prog);

		if(prog == 0) continue;

		m_Real->glGetProgramStageiv(prog, shs[s], eGL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS, &Subroutines[s].numSubroutines);

		for(GLint i=0; i < Subroutines[s].numSubroutines; i++)
			m_Real->glGetUniformSubroutineuiv(shs[s], i, &Subroutines[s].Values[s]);
	}

	m_Real->glGetIntegerv(eGL_ARRAY_BUFFER_BINDING,              (GLint*)&BufferBindings[eBufIdx_Array]);
	m_Real->glGetIntegerv(eGL_COPY_READ_BUFFER_BINDING,          (GLint*)&BufferBindings[eBufIdx_Copy_Read]);
	m_Real->glGetIntegerv(eGL_COPY_WRITE_BUFFER_BINDING,         (GLint*)&BufferBindings[eBufIdx_Copy_Write]);
	m_Real->glGetIntegerv(eGL_DRAW_INDIRECT_BUFFER_BINDING,      (GLint*)&BufferBindings[eBufIdx_Draw_Indirect]);
	m_Real->glGetIntegerv(eGL_DISPATCH_INDIRECT_BUFFER_BINDING,  (GLint*)&BufferBindings[eBufIdx_Dispatch_Indirect]);
	m_Real->glGetIntegerv(eGL_PIXEL_PACK_BUFFER_BINDING,         (GLint*)&BufferBindings[eBufIdx_Pixel_Pack]);
	m_Real->glGetIntegerv(eGL_PIXEL_UNPACK_BUFFER_BINDING,       (GLint*)&BufferBindings[eBufIdx_Pixel_Unpack]);
	m_Real->glGetIntegerv(eGL_QUERY_BUFFER_BINDING,              (GLint*)&BufferBindings[eBufIdx_Query]);
	m_Real->glGetIntegerv(eGL_TEXTURE_BUFFER_BINDING,            (GLint*)&BufferBindings[eBufIdx_Texture]);

	struct { IdxRangeBuffer *bufs; int count; GLenum binding; GLenum start; GLenum size; GLenum maxcount; } idxBufs[] =
	{
		{ AtomicCounter, ARRAY_COUNT(AtomicCounter), eGL_ATOMIC_COUNTER_BUFFER_BINDING, eGL_ATOMIC_COUNTER_BUFFER_START, eGL_ATOMIC_COUNTER_BUFFER_SIZE, eGL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, },
		{ ShaderStorage, ARRAY_COUNT(ShaderStorage), eGL_SHADER_STORAGE_BUFFER_BINDING, eGL_SHADER_STORAGE_BUFFER_START, eGL_SHADER_STORAGE_BUFFER_SIZE, eGL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, },
		{ TransformFeedback, ARRAY_COUNT(TransformFeedback), eGL_TRANSFORM_FEEDBACK_BUFFER_BINDING, eGL_TRANSFORM_FEEDBACK_BUFFER_START, eGL_TRANSFORM_FEEDBACK_BUFFER_SIZE, eGL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, },
		{ UniformBinding, ARRAY_COUNT(UniformBinding), eGL_UNIFORM_BUFFER_BINDING, eGL_UNIFORM_BUFFER_START, eGL_UNIFORM_BUFFER_SIZE, eGL_MAX_UNIFORM_BUFFER_BINDINGS, },
	};

	for(GLuint b=0; b < (GLuint)ARRAY_COUNT(idxBufs); b++)
	{
		GLint maxCount = 0;
		m_Real->glGetIntegerv(idxBufs[b].maxcount, &maxCount);
		for(int i=0; i < idxBufs[b].count && i < maxCount; i++)
		{
			m_Real->glGetIntegeri_v(idxBufs[b].binding, i, (GLint*)&idxBufs[b].bufs[i].name);
			m_Real->glGetInteger64i_v(idxBufs[b].start, i, (GLint64*)&idxBufs[b].bufs[i].start);
			m_Real->glGetInteger64i_v(idxBufs[b].size,  i, (GLint64*)&idxBufs[b].bufs[i].size);
		}
	}
	
	for(GLuint i=0; i < (GLuint)ARRAY_COUNT(Blends); i++)
	{
		m_Real->glGetIntegeri_v(eGL_BLEND_EQUATION_RGB, i, (GLint*)&Blends[i].EquationRGB);
		m_Real->glGetIntegeri_v(eGL_BLEND_EQUATION_ALPHA, i, (GLint*)&Blends[i].EquationAlpha);

		m_Real->glGetIntegeri_v(eGL_BLEND_SRC_RGB, i, (GLint*)&Blends[i].SourceRGB);
		m_Real->glGetIntegeri_v(eGL_BLEND_SRC_ALPHA, i, (GLint*)&Blends[i].SourceAlpha);

		m_Real->glGetIntegeri_v(eGL_BLEND_DST_RGB, i, (GLint*)&Blends[i].DestinationRGB);
		m_Real->glGetIntegeri_v(eGL_BLEND_DST_ALPHA, i, (GLint*)&Blends[i].DestinationAlpha);

		Blends[i].Enabled = (m_Real->glIsEnabledi(eGL_BLEND, i) == GL_TRUE);
	}

	m_Real->glGetFloatv(eGL_BLEND_COLOR, &BlendColor[0]);

	for(GLuint i=0; i < (GLuint)ARRAY_COUNT(Viewports); i++)
		m_Real->glGetFloati_v(eGL_VIEWPORT, i, &Viewports[i].x);
	
	for(GLuint i=0; i < (GLuint)ARRAY_COUNT(Scissors); i++)
	{
		m_Real->glGetIntegeri_v(eGL_SCISSOR_BOX, i, &Scissors[i].x);
		Scissors[i].enabled = (m_Real->glIsEnabledi(eGL_SCISSOR_TEST, i) == GL_TRUE);
	}

	m_Real->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, (GLint *)&DrawFBO);
	m_Real->glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, (GLint *)&ReadFBO);

	m_Real->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, 0);
	m_Real->glBindFramebuffer(eGL_READ_FRAMEBUFFER, 0);

	for(size_t i=0; i < ARRAY_COUNT(DrawBuffers); i++)
		m_Real->glGetIntegerv(GLenum(eGL_DRAW_BUFFER0 + i), (GLint *)&DrawBuffers[i]);

	m_Real->glGetIntegerv(eGL_READ_BUFFER, (GLint *)&ReadBuffer);

	m_Real->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, DrawFBO);
	m_Real->glBindFramebuffer(eGL_READ_FRAMEBUFFER, ReadFBO);

	m_Real->glGetIntegerv(eGL_FRAGMENT_SHADER_DERIVATIVE_HINT, (GLint *)&Hints.Derivatives);
	m_Real->glGetIntegerv(eGL_LINE_SMOOTH_HINT, (GLint *)&Hints.LineSmooth);
	m_Real->glGetIntegerv(eGL_POLYGON_SMOOTH_HINT, (GLint *)&Hints.PolySmooth);
	m_Real->glGetIntegerv(eGL_TEXTURE_COMPRESSION_HINT, (GLint *)&Hints.TexCompression);
	
	m_Real->glGetBooleanv(eGL_DEPTH_WRITEMASK, &DepthWriteMask);
	m_Real->glGetFloatv(eGL_DEPTH_CLEAR_VALUE, &DepthClearValue);
	m_Real->glGetIntegerv(eGL_DEPTH_FUNC, (GLint *)&DepthFunc);
	
	for(GLuint i=0; i < (GLuint)ARRAY_COUNT(DepthRanges); i++)
		m_Real->glGetDoublei_v(eGL_DEPTH_RANGE, i, &DepthRanges[i].nearZ);
	
	m_Real->glGetDoublev(eGL_DEPTH_BOUNDS_TEST_EXT, &DepthBounds.nearZ);

	{
		m_Real->glGetIntegerv(eGL_STENCIL_FUNC, (GLint *)&StencilFront.func);
		m_Real->glGetIntegerv(eGL_STENCIL_BACK_FUNC, (GLint *)&StencilBack.func);

		m_Real->glGetIntegerv(eGL_STENCIL_REF, (GLint *)&StencilFront.ref);
		m_Real->glGetIntegerv(eGL_STENCIL_BACK_REF, (GLint *)&StencilBack.ref);

		GLint maskval;
		m_Real->glGetIntegerv(eGL_STENCIL_VALUE_MASK, &maskval); StencilFront.valuemask = uint8_t(maskval&0xff);
		m_Real->glGetIntegerv(eGL_STENCIL_BACK_VALUE_MASK, &maskval); StencilBack.valuemask = uint8_t(maskval&0xff);
		
		m_Real->glGetIntegerv(eGL_STENCIL_WRITEMASK, &maskval); StencilFront.writemask = uint8_t(maskval&0xff);
		m_Real->glGetIntegerv(eGL_STENCIL_BACK_WRITEMASK, &maskval); StencilBack.writemask = uint8_t(maskval&0xff);

		m_Real->glGetIntegerv(eGL_STENCIL_FAIL, (GLint *)&StencilFront.stencilFail);
		m_Real->glGetIntegerv(eGL_STENCIL_BACK_FAIL, (GLint *)&StencilBack.stencilFail);

		m_Real->glGetIntegerv(eGL_STENCIL_PASS_DEPTH_FAIL, (GLint *)&StencilFront.depthFail);
		m_Real->glGetIntegerv(eGL_STENCIL_BACK_PASS_DEPTH_FAIL, (GLint *)&StencilBack.depthFail);

		m_Real->glGetIntegerv(eGL_STENCIL_PASS_DEPTH_PASS, (GLint *)&StencilFront.pass);
		m_Real->glGetIntegerv(eGL_STENCIL_BACK_PASS_DEPTH_PASS, (GLint *)&StencilBack.pass);
	}

	m_Real->glGetIntegerv(eGL_STENCIL_CLEAR_VALUE, (GLint *)&StencilClearValue);
	
	for(size_t i=0; i < ARRAY_COUNT(ColorMasks); i++)
		m_Real->glGetBooleanv(eGL_COLOR_WRITEMASK, &ColorMasks[i].red);

	m_Real->glGetIntegeri_v(eGL_SAMPLE_MASK_VALUE, 0, (GLint *)&SampleMask[0]);
	m_Real->glGetIntegerv(eGL_SAMPLE_COVERAGE_VALUE, (GLint *)&SampleCoverage);
	m_Real->glGetIntegerv(eGL_SAMPLE_COVERAGE_INVERT, (GLint *)&boolread); SampleCoverageInvert = (boolread != 0);
	m_Real->glGetFloatv(eGL_MIN_SAMPLE_SHADING_VALUE, &MinSampleShading);

	if(ExtensionSupported[ExtensionSupported_EXT_raster_multisample])
		m_Real->glGetIntegerv(eGL_RASTER_SAMPLES_EXT, (GLint *)&RasterSamples);
	else
		RasterSamples = 0;
	
	if(ExtensionSupported[ExtensionSupported_EXT_raster_multisample])
		m_Real->glGetIntegerv(eGL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT, (GLint *)&RasterFixed);
	else
		RasterFixed = false;
	
	m_Real->glGetIntegerv(eGL_LOGIC_OP_MODE, (GLint *)&LogicOp);

	m_Real->glGetFloatv(eGL_COLOR_CLEAR_VALUE, &ColorClearValue.red);
	
	m_Real->glGetIntegerv(eGL_PATCH_VERTICES, &PatchParams.numVerts);
	m_Real->glGetFloatv(eGL_PATCH_DEFAULT_INNER_LEVEL, &PatchParams.defaultInnerLevel[0]);
	m_Real->glGetFloatv(eGL_PATCH_DEFAULT_OUTER_LEVEL, &PatchParams.defaultOuterLevel[0]);
	
	if(!VendorCheck[VendorCheck_AMD_polygon_mode_query])
	{
		// This was listed in docs as enumeration[2] even though polygon mode can't be set independently for front
		// and back faces for a while, so pass large enough array to be sure.
		// AMD driver claims this doesn't exist anymore in core, so don't return any value, set to
		// default GL_FILL to be safe
		GLenum dummy[2] = { eGL_FILL, eGL_FILL };
		m_Real->glGetIntegerv(eGL_POLYGON_MODE, (GLint *)&dummy);
		PolygonMode = dummy[0];
	}
	else
	{
		PolygonMode = eGL_FILL;
	}
	
	m_Real->glGetFloatv(eGL_POLYGON_OFFSET_FACTOR, &PolygonOffset[0]);
	m_Real->glGetFloatv(eGL_POLYGON_OFFSET_UNITS, &PolygonOffset[1]);
	if(ExtensionSupported[ExtensionSupported_EXT_polygon_offset_clamp])
		m_Real->glGetFloatv(eGL_POLYGON_OFFSET_CLAMP_EXT, &PolygonOffset[2]);
	else
		PolygonOffset[2] = 0.0f;

	m_Real->glGetIntegerv(eGL_FRONT_FACE, (GLint *)&FrontFace);
	m_Real->glGetIntegerv(eGL_CULL_FACE_MODE, (GLint *)&CullFace);
}
Example #12
0
void GLRenderState::ApplyState(void *ctx, WrappedOpenGL *gl)
{
	{
		GLenum pnames[] =
		{
			eGL_CLIP_DISTANCE0,
			eGL_CLIP_DISTANCE1,
			eGL_CLIP_DISTANCE2,
			eGL_CLIP_DISTANCE3,
			eGL_CLIP_DISTANCE4,
			eGL_CLIP_DISTANCE5,
			eGL_CLIP_DISTANCE6,
			eGL_CLIP_DISTANCE7,
			eGL_COLOR_LOGIC_OP,
			eGL_CULL_FACE,
			eGL_DEPTH_CLAMP,
			eGL_DEPTH_TEST,
			eGL_DEPTH_BOUNDS_TEST_EXT,
			eGL_DITHER,
			eGL_FRAMEBUFFER_SRGB,
			eGL_LINE_SMOOTH,
			eGL_MULTISAMPLE,
			eGL_POLYGON_SMOOTH,
			eGL_POLYGON_OFFSET_FILL,
			eGL_POLYGON_OFFSET_LINE,
			eGL_POLYGON_OFFSET_POINT,
			eGL_PROGRAM_POINT_SIZE,
			eGL_PRIMITIVE_RESTART,
			eGL_PRIMITIVE_RESTART_FIXED_INDEX,
			eGL_SAMPLE_ALPHA_TO_COVERAGE,
			eGL_SAMPLE_ALPHA_TO_ONE,
			eGL_SAMPLE_COVERAGE,
			eGL_SAMPLE_MASK,
			eGL_RASTER_MULTISAMPLE_EXT,
			eGL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT,
			eGL_STENCIL_TEST,
			eGL_TEXTURE_CUBE_MAP_SEAMLESS,
			eGL_BLEND_ADVANCED_COHERENT_KHR,
		};
		
		RDCCOMPILE_ASSERT(ARRAY_COUNT(pnames) == eEnabled_Count, "Wrong number of pnames");
		
		for(GLuint i=0; i < eEnabled_Count; i++)
		{
			if(pnames[i] == eGL_BLEND_ADVANCED_COHERENT_KHR && !ExtensionSupported[ExtensionSupported_KHR_blend_equation_advanced_coherent])
				continue;
			
			if((pnames[i] == eGL_RASTER_MULTISAMPLE_EXT || pnames[i] == eGL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT)
				 && !ExtensionSupported[ExtensionSupported_EXT_raster_multisample])
				continue;

			if(Enabled[i]) m_Real->glEnable(pnames[i]); else m_Real->glDisable(pnames[i]);
		}
	}

	for(GLuint i=0; i < (GLuint)ARRAY_COUNT(Tex2D); i++)
	{
		m_Real->glActiveTexture(GLenum(eGL_TEXTURE0 + i));
		m_Real->glBindTexture(eGL_TEXTURE_2D, Tex2D[i]);
		m_Real->glBindSampler(i, Samplers[i]);
	}
	
	m_Real->glActiveTexture(ActiveTexture);

	m_Real->glBindVertexArray(VAO);
	m_Real->glBindTransformFeedback(eGL_TRANSFORM_FEEDBACK, FeedbackObj);
	
	// See FetchState(). The spec says that you have to SET the right format for the shader too,
	// but we couldn't query for the format so we can't set it here.
	GLuint maxNumAttribs = 0;
	m_Real->glGetIntegerv(eGL_MAX_VERTEX_ATTRIBS, (GLint *)&maxNumAttribs);
	for(GLuint i=0; i < RDCMIN(maxNumAttribs, (GLuint)ARRAY_COUNT(GenericVertexAttribs)); i++)
		m_Real->glVertexAttrib4fv(i, &GenericVertexAttribs[i].x);
	
	m_Real->glPointParameterf(eGL_POINT_FADE_THRESHOLD_SIZE, PointFadeThresholdSize);
	m_Real->glPointParameteri(eGL_POINT_SPRITE_COORD_ORIGIN, (GLint)PointSpriteOrigin);
	m_Real->glLineWidth(LineWidth);
	m_Real->glPointSize(PointSize);
	
	m_Real->glPrimitiveRestartIndex(PrimitiveRestartIndex);
	if(m_Real->glClipControl) // only available in 4.5+
		m_Real->glClipControl(ClipOrigin, ClipDepth);
	m_Real->glProvokingVertex(ProvokingVertex);

	m_Real->glUseProgram(Program);
	m_Real->glBindProgramPipeline(Pipeline);
	
	GLenum shs[] = {
		eGL_VERTEX_SHADER,
		eGL_TESS_CONTROL_SHADER,
		eGL_TESS_EVALUATION_SHADER,
		eGL_GEOMETRY_SHADER,
		eGL_FRAGMENT_SHADER,
		eGL_COMPUTE_SHADER
	};

	RDCCOMPILE_ASSERT(ARRAY_COUNT(shs) == ARRAY_COUNT(Subroutines), "Subroutine array not the right size");
	for(size_t s=0; s < ARRAY_COUNT(shs); s++)
		if(Subroutines[s].numSubroutines > 0)
			m_Real->glUniformSubroutinesuiv(shs[s], Subroutines[s].numSubroutines, Subroutines[s].Values);

	m_Real->glBindBuffer(eGL_ARRAY_BUFFER,              BufferBindings[eBufIdx_Array]);
	m_Real->glBindBuffer(eGL_COPY_READ_BUFFER,          BufferBindings[eBufIdx_Copy_Read]);
	m_Real->glBindBuffer(eGL_COPY_WRITE_BUFFER,         BufferBindings[eBufIdx_Copy_Write]);
	m_Real->glBindBuffer(eGL_DRAW_INDIRECT_BUFFER,      BufferBindings[eBufIdx_Draw_Indirect]);
	m_Real->glBindBuffer(eGL_DISPATCH_INDIRECT_BUFFER,  BufferBindings[eBufIdx_Dispatch_Indirect]);
	m_Real->glBindBuffer(eGL_PIXEL_PACK_BUFFER,         BufferBindings[eBufIdx_Pixel_Pack]);
	m_Real->glBindBuffer(eGL_PIXEL_UNPACK_BUFFER,       BufferBindings[eBufIdx_Pixel_Unpack]);
	m_Real->glBindBuffer(eGL_QUERY_BUFFER,              BufferBindings[eBufIdx_Query]);
	m_Real->glBindBuffer(eGL_TEXTURE_BUFFER,            BufferBindings[eBufIdx_Texture]);

	struct { IdxRangeBuffer *bufs; int count; GLenum binding; GLenum maxcount; } idxBufs[] =
	{
		{ AtomicCounter, ARRAY_COUNT(AtomicCounter), eGL_ATOMIC_COUNTER_BUFFER, eGL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, },
		{ ShaderStorage, ARRAY_COUNT(ShaderStorage), eGL_SHADER_STORAGE_BUFFER, eGL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, },
		{ TransformFeedback, ARRAY_COUNT(TransformFeedback), eGL_TRANSFORM_FEEDBACK_BUFFER, eGL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, },
		{ UniformBinding, ARRAY_COUNT(UniformBinding), eGL_UNIFORM_BUFFER, eGL_MAX_UNIFORM_BUFFER_BINDINGS, },
	};

	for(size_t b=0; b < ARRAY_COUNT(idxBufs); b++)
	{
		// only restore buffer bindings here if we were using the default transform feedback object
		if(idxBufs[b].binding == eGL_TRANSFORM_FEEDBACK_BUFFER && FeedbackObj) continue;

		GLint maxCount = 0;
		m_Real->glGetIntegerv(idxBufs[b].maxcount, &maxCount);
		for(int i=0; i < idxBufs[b].count && i < maxCount; i++)
		{
			if(idxBufs[b].bufs[i].name == 0 ||
					(idxBufs[b].bufs[i].start == 0 && idxBufs[b].bufs[i].size == 0)
				)
				m_Real->glBindBufferBase(idxBufs[b].binding, i, idxBufs[b].bufs[i].name);
			else
				m_Real->glBindBufferRange(idxBufs[b].binding, i, idxBufs[b].bufs[i].name, (GLintptr)idxBufs[b].bufs[i].start, (GLsizeiptr)idxBufs[b].bufs[i].size);
		}
	}
	
	for(GLuint i=0; i < (GLuint)ARRAY_COUNT(Blends); i++)
	{
		m_Real->glBlendFuncSeparatei(i, Blends[i].SourceRGB, Blends[i].DestinationRGB, Blends[i].SourceAlpha, Blends[i].DestinationAlpha);
		m_Real->glBlendEquationSeparatei(i, Blends[i].EquationRGB, Blends[i].EquationAlpha);
		
		if(Blends[i].Enabled)
			m_Real->glEnablei(eGL_BLEND, i);
		else
			m_Real->glDisablei(eGL_BLEND, i);
	}

	m_Real->glBlendColor(BlendColor[0], BlendColor[1], BlendColor[2], BlendColor[3]);

	m_Real->glViewportArrayv(0, ARRAY_COUNT(Viewports), &Viewports[0].x);

	for (GLuint s = 0; s < (GLuint)ARRAY_COUNT(Scissors); ++s)
	{
		m_Real->glScissorIndexedv(s, &Scissors[s].x);
	
		if (Scissors[s].enabled)
			m_Real->glEnablei(eGL_SCISSOR_TEST, s);
		else
			m_Real->glDisablei(eGL_SCISSOR_TEST, s);
	}

	GLenum DBs[8] = { eGL_NONE };
	uint32_t numDBs = 0;
	for(GLuint i=0; i < (GLuint)ARRAY_COUNT(DrawBuffers); i++)
	{
		if(DrawBuffers[i] != eGL_NONE)
		{
			numDBs++;
			DBs[i] = DrawBuffers[i];

			if(m_State < WRITING)
			{
				// since we are faking the default framebuffer with our own
				// to see the results, replace back/front/left/right with color attachment 0
				if(DBs[i] == eGL_BACK_LEFT || DBs[i] == eGL_BACK_RIGHT ||
					DBs[i] == eGL_FRONT_LEFT || DBs[i] == eGL_FRONT_RIGHT)
					DBs[i] = eGL_COLOR_ATTACHMENT0;

				// These aren't valid for glDrawBuffers but can be returned when we call glGet,
				// assume they mean left implicitly
				if(DBs[i] == eGL_BACK ||
					DBs[i] == eGL_FRONT)
					DBs[i] = eGL_COLOR_ATTACHMENT0;
			}
		}
		else
		{
			break;
		}
	}

	// apply drawbuffers/readbuffer to default framebuffer
	m_Real->glBindFramebuffer(eGL_READ_FRAMEBUFFER, gl->GetFakeBBFBO());
	m_Real->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, gl->GetFakeBBFBO());
	m_Real->glDrawBuffers(numDBs, DBs);

	// see above for reasoning for this
	m_Real->glReadBuffer(eGL_COLOR_ATTACHMENT0);

	m_Real->glBindFramebuffer(eGL_READ_FRAMEBUFFER, ReadFBO);
	m_Real->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, DrawFBO);
	
	m_Real->glHint(eGL_FRAGMENT_SHADER_DERIVATIVE_HINT, Hints.Derivatives);
	m_Real->glHint(eGL_LINE_SMOOTH_HINT, Hints.LineSmooth);
	m_Real->glHint(eGL_POLYGON_SMOOTH_HINT, Hints.PolySmooth);
	m_Real->glHint(eGL_TEXTURE_COMPRESSION_HINT, Hints.TexCompression);
	
	m_Real->glDepthMask(DepthWriteMask);
	m_Real->glClearDepth(DepthClearValue);
	m_Real->glDepthFunc(DepthFunc);
	
	for(GLuint i=0; i < (GLuint)ARRAY_COUNT(DepthRanges); i++)
	{
		double v[2] = { DepthRanges[i].nearZ, DepthRanges[i].farZ };
		m_Real->glDepthRangeArrayv(i, 1, v);
	}

	if(m_Real->glDepthBoundsEXT) // extension, not always available
		m_Real->glDepthBoundsEXT(DepthBounds.nearZ, DepthBounds.farZ);
	
	{
		m_Real->glStencilFuncSeparate(eGL_FRONT, StencilFront.func, StencilFront.ref, StencilFront.valuemask);
		m_Real->glStencilFuncSeparate(eGL_BACK, StencilBack.func, StencilBack.ref, StencilBack.valuemask);

		m_Real->glStencilMaskSeparate(eGL_FRONT, StencilFront.writemask);
		m_Real->glStencilMaskSeparate(eGL_BACK, StencilBack.writemask);

		m_Real->glStencilOpSeparate(eGL_FRONT, StencilFront.stencilFail, StencilFront.depthFail, StencilFront.pass);
		m_Real->glStencilOpSeparate(eGL_BACK, StencilBack.stencilFail, StencilBack.depthFail, StencilBack.pass);
	}

	m_Real->glClearStencil((GLint)StencilClearValue);
	
	for(GLuint i=0; i < (GLuint)ARRAY_COUNT(ColorMasks); i++)
		m_Real->glColorMaski(i, ColorMasks[i].red, ColorMasks[i].green, ColorMasks[i].blue, ColorMasks[i].alpha);

	m_Real->glSampleMaski(0, (GLbitfield)SampleMask[0]);
	m_Real->glSampleCoverage(SampleCoverage, SampleCoverageInvert ? GL_TRUE : GL_FALSE);
	m_Real->glMinSampleShading(MinSampleShading);

	if(ExtensionSupported[ExtensionSupported_EXT_raster_multisample])
		m_Real->glRasterSamplesEXT(RasterSamples, RasterFixed);

	m_Real->glLogicOp(LogicOp);

	m_Real->glClearColor(ColorClearValue.red, ColorClearValue.green, ColorClearValue.blue, ColorClearValue.alpha);
	
	m_Real->glPatchParameteri(eGL_PATCH_VERTICES, PatchParams.numVerts);
	m_Real->glPatchParameterfv(eGL_PATCH_DEFAULT_INNER_LEVEL, PatchParams.defaultInnerLevel);
	m_Real->glPatchParameterfv(eGL_PATCH_DEFAULT_OUTER_LEVEL, PatchParams.defaultOuterLevel);

	m_Real->glPolygonMode(eGL_FRONT_AND_BACK, PolygonMode);
	if(ExtensionSupported[ExtensionSupported_EXT_polygon_offset_clamp])
		m_Real->glPolygonOffsetClampEXT(PolygonOffset[0], PolygonOffset[1], PolygonOffset[2]);
	else
		m_Real->glPolygonOffset(PolygonOffset[0], PolygonOffset[1]);

	m_Real->glFrontFace(FrontFace);
	m_Real->glCullFace(CullFace);
}