Пример #1
0
VkResult WrappedVulkan::vkCreateDisplayModeKHR(VkPhysicalDevice physicalDevice, VkDisplayKHR display,
                                               const VkDisplayModeCreateInfoKHR *pCreateInfo,
                                               const VkAllocationCallbacks *pAllocator,
                                               VkDisplayModeKHR *pMode)
{
  // we don't wrap the resulting mode since there's no data we need for it
  return ObjDisp(physicalDevice)
      ->CreateDisplayModeKHR(Unwrap(physicalDevice), display, pCreateInfo, pAllocator, pMode);
}
Пример #2
0
BOOL WrappedOpenGL::wglDXUnregisterObjectNV(HANDLE hDevice, HANDLE hObject)
{
  // don't need to intercept this, as the DX and GL textures will be deleted independently
  BOOL ret = m_Real.wglDXUnregisterObjectNV(hDevice, Unwrap(hObject));

  delete(WrappedHANDLE *)hObject;

  return ret;
}
Пример #3
0
BOOL WrappedOpenGL::wglDXUnlockObjectsNV(HANDLE hDevice, GLint count, HANDLE *hObjects)
{
  HANDLE *unwrapped = new HANDLE[count];
  for(GLint i = 0; i < count; i++)
    unwrapped[i] = Unwrap(hObjects[i]);
  BOOL ret = m_Real.wglDXUnlockObjectsNV(hDevice, count, unwrapped);
  SAFE_DELETE_ARRAY(unwrapped);
  return ret;
}
Пример #4
0
VkResult WrappedVulkan::vkCreateDebugReportCallbackEXT(
		VkInstance                                  instance,
    const VkDebugReportCallbackCreateInfoEXT*   pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkDebugReportCallbackEXT*                   pCallback)
{
	// don't need to wrap this, as we will create our own independent callback
	return ObjDisp(instance)->CreateDebugReportCallbackEXT(Unwrap(instance), pCreateInfo, pAllocator, pCallback);
}
Пример #5
0
VkResult WrappedVulkan::vkGetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice physicalDevice,
                                                              uint32_t planeIndex,
                                                              uint32_t *pDisplayCount,
                                                              VkDisplayKHR *pDisplays)
{
  // we don't wrap the resulting displays since there's no data we need for them
  return ObjDisp(physicalDevice)
      ->GetDisplayPlaneSupportedDisplaysKHR(Unwrap(physicalDevice), planeIndex, pDisplayCount,
                                            pDisplays);
}
Пример #6
0
VkResult WrappedVulkan::vkResetDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool,
                                              VkDescriptorPoolResetFlags flags)
{
  // need to free all child descriptor pools. Application is responsible for
  // ensuring no concurrent use with alloc/free from this pool, the same as
  // for DestroyDescriptorPool.
  VkResourceRecord *record = GetRecord(descriptorPool);

  // delete all of the children
  for(auto it = record->pooledChildren.begin(); it != record->pooledChildren.end(); ++it)
  {
    // unset record->pool so we don't recurse
    (*it)->pool = NULL;
    GetResourceManager()->ReleaseWrappedResource((VkDescriptorSet)(uint64_t)(*it)->Resource, true);
  }
  record->pooledChildren.clear();

  return ObjDisp(device)->ResetDescriptorPool(Unwrap(device), Unwrap(descriptorPool), flags);
}
Пример #7
0
static void
AnimCurScreenBlockHandler(int screenNum,
                          pointer blockData,
                          pointer pTimeout, pointer pReadmask)
{
    ScreenPtr pScreen = screenInfo.screens[screenNum];
    AnimCurScreenPtr as = GetAnimCurScreen(pScreen);
    DeviceIntPtr dev;
    Bool activeDevice = FALSE;
    CARD32 now = 0, soonest = ~0;       /* earliest time to wakeup again */

    for (dev = inputInfo.devices; dev; dev = dev->next) {
        if (IsPointerDevice(dev) && pScreen == dev->spriteInfo->anim.pScreen) {
            if (!activeDevice) {
                now = GetTimeInMillis();
                activeDevice = TRUE;
            }

            if ((INT32) (now - dev->spriteInfo->anim.time) >= 0) {
                AnimCurPtr ac = GetAnimCur(dev->spriteInfo->anim.pCursor);
                int elt = (dev->spriteInfo->anim.elt + 1) % ac->nelt;
                DisplayCursorProcPtr DisplayCursor;

                /*
                 * Not a simple Unwrap/Wrap as this
                 * isn't called along the DisplayCursor
                 * wrapper chain.
                 */
                DisplayCursor = pScreen->DisplayCursor;
                pScreen->DisplayCursor = as->DisplayCursor;
                (void) (*pScreen->DisplayCursor) (dev,
                                                  pScreen,
                                                  ac->elts[elt].pCursor);
                as->DisplayCursor = pScreen->DisplayCursor;
                pScreen->DisplayCursor = DisplayCursor;

                dev->spriteInfo->anim.elt = elt;
                dev->spriteInfo->anim.time = now + ac->elts[elt].delay;
            }

            if (soonest > dev->spriteInfo->anim.time)
                soonest = dev->spriteInfo->anim.time;
        }
    }

    if (activeDevice)
        AdjustWaitForDelay(pTimeout, soonest - now);

    Unwrap(as, pScreen, BlockHandler);
    (*pScreen->BlockHandler) (screenNum, blockData, pTimeout, pReadmask);
    if (activeDevice)
        Wrap(as, pScreen, BlockHandler, AnimCurScreenBlockHandler);
    else
        as->BlockHandler = NULL;
}
Пример #8
0
VkResult WrappedVulkan::vkRegisterDeviceEventEXT(VkDevice device,
                                                 const VkDeviceEventInfoEXT *pDeviceEventInfo,
                                                 const VkAllocationCallbacks *pAllocator,
                                                 VkFence *pFence)
{
  // for now we emulate this on replay as just a regular fence create, since we don't faithfully
  // replay sync events anyway.
  VkResult ret =
      ObjDisp(device)->RegisterDeviceEventEXT(Unwrap(device), pDeviceEventInfo, pAllocator, pFence);

  if(ret == VK_SUCCESS)
  {
    ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), *pFence);

    if(m_State >= WRITING)
    {
      Chunk *chunk = NULL;

      {
        CACHE_THREAD_SERIALISER();

        VkFenceCreateInfo createInfo = {
            VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, NULL, VK_FENCE_CREATE_SIGNALED_BIT,
        };

        SCOPED_SERIALISE_CONTEXT(CREATE_FENCE);
        Serialise_vkCreateFence(localSerialiser, device, &createInfo, NULL, pFence);

        chunk = scope.Get();
      }

      VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pFence);
      record->AddChunk(chunk);
    }
    else
    {
      GetResourceManager()->AddLiveResource(id, *pFence);
    }
  }

  return ret;
}
Пример #9
0
void WrappedID3D12Device::GetResourceTiling(
    ID3D12Resource *pTiledResource, UINT *pNumTilesForEntireResource,
    D3D12_PACKED_MIP_INFO *pPackedMipDesc, D3D12_TILE_SHAPE *pStandardTileShapeForNonPackedMips,
    UINT *pNumSubresourceTilings, UINT FirstSubresourceTilingToGet,
    D3D12_SUBRESOURCE_TILING *pSubresourceTilingsForNonPackedMips)
{
  return m_pDevice->GetResourceTiling(Unwrap(pTiledResource), pNumTilesForEntireResource,
                                      pPackedMipDesc, pStandardTileShapeForNonPackedMips,
                                      pNumSubresourceTilings, FirstSubresourceTilingToGet,
                                      pSubresourceTilingsForNonPackedMips);
}
Пример #10
0
VkResult WrappedVulkan::vkCreateSwapchainKHR(VkDevice device,
                                             const VkSwapchainCreateInfoKHR *pCreateInfo,
                                             const VkAllocationCallbacks *pAllocator,
                                             VkSwapchainKHR *pSwapChain)
{
  VkSwapchainCreateInfoKHR createInfo = *pCreateInfo;

  // make sure we can readback to get the screenshot, and render to it for the text overlay
  createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
  createInfo.surface = Unwrap(createInfo.surface);
  createInfo.oldSwapchain = Unwrap(createInfo.oldSwapchain);

  VkResult ret =
      ObjDisp(device)->CreateSwapchainKHR(Unwrap(device), &createInfo, pAllocator, pSwapChain);

  if(ret == VK_SUCCESS)
    WrapAndProcessCreatedSwapchain(device, pCreateInfo, pSwapChain);

  return ret;
}
Пример #11
0
v8::Handle<v8::Value> CJavaArray::IndexedGetter(
  uint32_t index, const v8::AccessorInfo& info)
{
  CJavaArray& obj = Unwrap(info.Holder());

  jni::V8Env env(obj.m_pEnv);

  static jmethodID mid = env.GetStaticMethodID(env.buildins.java.lang.reflect.Array, "get", "(Ljava/lang/Object;I)Ljava/lang/Object;");

  return env.Close(env.Wrap(env->CallStaticObjectMethod(env.buildins.java.lang.reflect.Array, mid, obj.m_obj, index)));
}
Пример #12
0
v8::Handle<v8::Value> CJavaObject::NamedGetter(
  v8::Local<v8::String> prop, const v8::AccessorInfo& info)
{
  CJavaObject& obj = Unwrap(info.Holder());

  jni::V8Env env(obj.m_pEnv);

  v8::String::Utf8Value name(prop);

  return env.Close(env.GetMember(obj.m_obj, *name));
}
Пример #13
0
VkResult WrappedVulkan::vkGetPhysicalDeviceExternalImageFormatPropertiesNV(
    VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling,
    VkImageUsageFlags usage, VkImageCreateFlags flags,
    VkExternalMemoryHandleTypeFlagsNV externalHandleType,
    VkExternalImageFormatPropertiesNV *pExternalImageFormatProperties)
{
  return ObjDisp(physicalDevice)
      ->GetPhysicalDeviceExternalImageFormatPropertiesNV(Unwrap(physicalDevice), format, type,
                                                         tiling, usage, flags, externalHandleType,
                                                         pExternalImageFormatProperties);
}
Пример #14
0
void WrappedVulkan::vkGetPhysicalDeviceMemoryProperties(
    VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties *pMemoryProperties)
{
  if(pMemoryProperties)
  {
    *pMemoryProperties = *GetRecord(physicalDevice)->memProps;
    return;
  }

  ObjDisp(physicalDevice)->GetPhysicalDeviceMemoryProperties(Unwrap(physicalDevice), pMemoryProperties);
}
Пример #15
0
void WrappedVulkan::vkGetImageMemoryRequirements(VkDevice device, VkImage image,
                                                 VkMemoryRequirements *pMemoryRequirements)
{
  ObjDisp(device)->GetImageMemoryRequirements(Unwrap(device), Unwrap(image), pMemoryRequirements);

  // don't do remapping here on replay.
  if(m_State < WRITING)
    return;

  uint32_t bits = pMemoryRequirements->memoryTypeBits;
  uint32_t *memIdxMap = GetRecord(device)->memIdxMap;

  pMemoryRequirements->memoryTypeBits = 0;

  // for each of our fake memory indices, check if the real
  // memory type it points to is set - if so, set our fake bit
  for(uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++)
    if(bits & (1 << memIdxMap[i]))
      pMemoryRequirements->memoryTypeBits |= (1 << i);
}
Пример #16
0
VkResult WrappedVulkan::vkGetEventStatus(
			VkDevice                                device,
			VkEvent                                 event)
{
	SCOPED_DBG_SINK();

	VkResult ret = ObjDisp(device)->GetEventStatus(Unwrap(device), Unwrap(event));
	
	if(m_State >= WRITING_CAPFRAME)
	{
		CACHE_THREAD_SERIALISER();

		SCOPED_SERIALISE_CONTEXT(GET_EVENT_STATUS);
		Serialise_vkGetEventStatus(localSerialiser, device, event);

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

	return ret;
}
VKAPI_ATTR VkResult VKAPI_CALL GetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkDisplayModeKHR mode,
                                                              uint32_t planeIndex, VkDisplayPlaneCapabilitiesKHR *pCapabilities) {
    instance_layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(physicalDevice), instance_layer_data_map);
    {
        std::lock_guard<std::mutex> lock(global_lock);
        mode = Unwrap(dev_data, mode);
    }
    VkResult result =
        dev_data->dispatch_table.GetDisplayPlaneCapabilitiesKHR(physicalDevice, mode, planeIndex, pCapabilities);
    return result;
}
Пример #18
0
void VulkanReplay::OutputWindow::CreateSurface(VkInstance inst)
{
  VkAndroidSurfaceCreateInfoKHR createInfo;

  createInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
  createInfo.pNext = NULL;
  createInfo.flags = 0;
  createInfo.window = wnd;

  VkResult vkr = ObjDisp(inst)->CreateAndroidSurfaceKHR(Unwrap(inst), &createInfo, NULL, &surface);
  RDCASSERTEQUAL(vkr, VK_SUCCESS);
}
Пример #19
0
void Path2D::LineTo(const v8::FunctionCallbackInfo<Value>& args) {
    if (args.Length() != 2) {
        args.GetIsolate()->ThrowException(
                v8::String::NewFromUtf8(
                        args.GetIsolate(), "Error: 2 arguments required."));
        return;
    }
    double x = args[0]->NumberValue();
    double y = args[1]->NumberValue();
    Path2D* path = Unwrap(args);
    path->fSkPath.lineTo(SkDoubleToScalar(x), SkDoubleToScalar(y));
}
Пример #20
0
v8::Handle<v8::Integer> CJavaObject::NamedQuery(
  v8::Local<v8::String> prop, const v8::AccessorInfo& info)
{
  CJavaObject& obj = Unwrap(info.Holder());

  jni::V8Env env(obj.m_pEnv);

  v8::String::Utf8Value name(prop);

  return env.HasMember(obj.m_obj, *name) ? env.Close(v8::Integer::New(v8::None)) : 
                                           v8::Handle<v8::Integer>();
}
Пример #21
0
void WrappedVulkan::vkDebugReportMessageEXT(
    VkInstance                                  instance,
    VkDebugReportFlagsEXT                       flags,
    VkDebugReportObjectTypeEXT                  objectType,
    uint64_t                                    object,
    size_t                                      location,
    int32_t                                     messageCode,
    const char*                                 pLayerPrefix,
    const char*                                 pMessage)
{
	return ObjDisp(instance)->DebugReportMessageEXT(Unwrap(instance), flags, objectType, object, location, messageCode, pLayerPrefix, pMessage);
}
Пример #22
0
bool WrappedVulkan::Serialise_vkDeviceWaitIdle(Serialiser* localSerialiser, VkDevice device)
{
	SERIALISE_ELEMENT(ResourceId, id, GetResID(device));
	
	if(m_State < WRITING)
	{
		device = GetResourceManager()->GetLiveHandle<VkDevice>(id);
		ObjDisp(device)->DeviceWaitIdle(Unwrap(device));
	}

	return true;
}
Пример #23
0
bool WrappedVulkan::Serialise_vkCreateBuffer(
		Serialiser*                                 localSerialiser,
			VkDevice                                    device,
			const VkBufferCreateInfo*                   pCreateInfo,
			const VkAllocationCallbacks*                pAllocator,
			VkBuffer*                                   pBuffer)
{
	SERIALISE_ELEMENT(ResourceId, devId, GetResID(device));
	SERIALISE_ELEMENT(VkBufferCreateInfo, info, *pCreateInfo);
	SERIALISE_ELEMENT(ResourceId, id, GetResID(*pBuffer));

	if(m_State == READING)
	{
		device = GetResourceManager()->GetLiveHandle<VkDevice>(devId);
		VkBuffer buf = VK_NULL_HANDLE;

		VkBufferUsageFlags origusage = info.usage;

		// ensure we can always readback from buffers
		info.usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT;

		VkResult ret = ObjDisp(device)->CreateBuffer(Unwrap(device), &info, NULL, &buf);

		info.usage = origusage;

		if(ret != VK_SUCCESS)
		{
			RDCERR("Failed on resource serialise-creation, VkResult: 0x%08x", ret);
		}
		else
		{
			ResourceId live = GetResourceManager()->WrapResource(Unwrap(device), buf);
			GetResourceManager()->AddLiveResource(id, buf);

			m_CreationInfo.m_Buffer[live].Init(GetResourceManager(), m_CreationInfo, &info);
		}
	}

	return true;
}
Пример #24
0
bool WrappedVulkan::Serialise_vkUnmapMemory(
		Serialiser*                                 localSerialiser,
    VkDevice                                    device,
    VkDeviceMemory                              mem)
{
	SERIALISE_ELEMENT(ResourceId, devId, GetResID(device));
	SERIALISE_ELEMENT(ResourceId, id, GetResID(mem));

	MemMapState *state;
	if(m_State >= WRITING)
		state = GetRecord(mem)->memMapState;

	SERIALISE_ELEMENT(uint64_t, memOffset, state->mapOffset);
	SERIALISE_ELEMENT(uint64_t, memSize, state->mapSize);
	SERIALISE_ELEMENT_BUF(byte*, data, (byte *)state->mappedPtr + state->mapOffset, (size_t)memSize);

	if(m_State < WRITING)
	{
		device = GetResourceManager()->GetLiveHandle<VkDevice>(devId);
		mem = GetResourceManager()->GetLiveHandle<VkDeviceMemory>(id);

		void *mapPtr = NULL;
		VkResult ret = ObjDisp(device)->MapMemory(Unwrap(device), Unwrap(mem), memOffset, memSize, 0, &mapPtr);

		if(ret != VK_SUCCESS)
		{
			RDCERR("Error mapping memory on replay: 0x%08x", ret);
		}
		else
		{
			memcpy((byte *)mapPtr, data, (size_t)memSize);

			ObjDisp(device)->UnmapMemory(Unwrap(device), Unwrap(mem));
		}

		SAFE_DELETE_ARRAY(data);
	}

	return true;
}
Пример #25
0
bool WrappedVulkan::Serialise_vkCmdSetScissor(Serialiser *localSerialiser,
                                              VkCommandBuffer cmdBuffer, uint32_t firstScissor,
                                              uint32_t scissorCount, const VkRect2D *pScissors)
{
  SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(cmdBuffer));
  SERIALISE_ELEMENT(uint32_t, first, firstScissor);
  SERIALISE_ELEMENT(uint32_t, count, scissorCount);
  SERIALISE_ELEMENT_ARR(VkRect2D, scissors, pScissors, count);

  Serialise_DebugMessages(localSerialiser, false);

  if(m_State < WRITING)
    m_LastCmdBufferID = cmdid;

  if(m_State == EXECUTING)
  {
    if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
    {
      cmdBuffer = RerecordCmdBuf(cmdid);
      ObjDisp(cmdBuffer)->CmdSetScissor(Unwrap(cmdBuffer), first, count, scissors);

      if(m_RenderState.scissors.size() < first + count)
        m_RenderState.scissors.resize(first + count);

      for(uint32_t i = 0; i < count; i++)
        m_RenderState.scissors[first + i] = scissors[i];
    }
  }
  else if(m_State == READING)
  {
    cmdBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);

    ObjDisp(cmdBuffer)->CmdSetScissor(Unwrap(cmdBuffer), first, count, scissors);
  }

  SAFE_DELETE_ARRAY(scissors);

  return true;
}
Пример #26
0
HRESULT STDMETHODCALLTYPE WrappedID3D12CommandQueue::Signal(ID3D12Fence *pFence, UINT64 Value)
{
  if(m_State == WRITING_CAPFRAME)
  {
    SCOPED_SERIALISE_CONTEXT(SIGNAL);
    Serialise_Signal(pFence, Value);

    m_QueueRecord->AddChunk(scope.Get());
    GetResourceManager()->MarkResourceFrameReferenced(GetResID(pFence), eFrameRef_Read);
  }

  return m_pReal->Signal(Unwrap(pFence), Value);
}
Пример #27
0
v8::Handle<v8::Value> CJavaArray::IndexedSetter(
  uint32_t index, v8::Local<v8::Value> value, const v8::AccessorInfo& info)
{
  CJavaArray& obj = Unwrap(info.Holder());

  jni::V8Env env(obj.m_pEnv);

  static jmethodID mid = env.GetStaticMethodID(env.buildins.java.lang.reflect.Array, "set", "(Ljava/lang/Object;ILjava/lang/Object;)V");

  env->CallStaticVoidMethod(env.buildins.java.lang.reflect.Array, mid, obj.m_obj, index, env.Wrap(value));

  return env.Close(value);
}
Пример #28
0
void WrappedVulkan::vkCmdResetEvent(
    VkCommandBuffer                                 cmdBuffer,
    VkEvent                                     event,
		VkPipelineStageFlags                        stageMask)
{
	SCOPED_DBG_SINK();

	ObjDisp(cmdBuffer)->CmdResetEvent(Unwrap(cmdBuffer), Unwrap(event), stageMask);

	if(m_State >= WRITING)
	{
		VkResourceRecord *record = GetRecord(cmdBuffer);

		CACHE_THREAD_SERIALISER();

		SCOPED_SERIALISE_CONTEXT(CMD_RESET_EVENT);
		Serialise_vkCmdResetEvent(localSerialiser, cmdBuffer, event, stageMask);

		record->AddChunk(scope.Get());
		record->MarkResourceFrameReferenced(GetResID(event), eFrameRef_Read);
	}
}
VKAPI_ATTR VkResult VKAPI_CALL CreateComputePipelines(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount,
                                                      const VkComputePipelineCreateInfo *pCreateInfos,
                                                      const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines) {
    layer_data *device_data = GetLayerDataPtr(get_dispatch_key(device), layer_data_map);
    safe_VkComputePipelineCreateInfo *local_pCreateInfos = NULL;
    if (pCreateInfos) {
        std::lock_guard<std::mutex> lock(global_lock);
        local_pCreateInfos = new safe_VkComputePipelineCreateInfo[createInfoCount];
        for (uint32_t idx0 = 0; idx0 < createInfoCount; ++idx0) {
            local_pCreateInfos[idx0].initialize(&pCreateInfos[idx0]);
            if (pCreateInfos[idx0].basePipelineHandle) {
                local_pCreateInfos[idx0].basePipelineHandle = Unwrap(device_data, pCreateInfos[idx0].basePipelineHandle);
            }
            if (pCreateInfos[idx0].layout) {
                local_pCreateInfos[idx0].layout = Unwrap(device_data, pCreateInfos[idx0].layout);
            }
            if (pCreateInfos[idx0].stage.module) {
                local_pCreateInfos[idx0].stage.module = Unwrap(device_data, pCreateInfos[idx0].stage.module);
            }
        }
    }
    if (pipelineCache) {
        std::lock_guard<std::mutex> lock(global_lock);
        pipelineCache = Unwrap(device_data, pipelineCache);
    }

    VkResult result = device_data->dispatch_table.CreateComputePipelines(
        device, pipelineCache, createInfoCount, local_pCreateInfos->ptr(), pAllocator, pPipelines);
    delete[] local_pCreateInfos;
    {
        std::lock_guard<std::mutex> lock(global_lock);
        for (uint32_t i = 0; i < createInfoCount; ++i) {
            if (pPipelines[i] != VK_NULL_HANDLE) {
                pPipelines[i] = WrapNew(device_data, pPipelines[i]);
            }
        }
    }
    return result;
}
Пример #30
0
bool WrappedVulkan::Serialise_vkCmdSetBlendConstants(Serialiser *localSerialiser,
                                                     VkCommandBuffer cmdBuffer,
                                                     const float *blendConst)
{
  SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(cmdBuffer));

  float blendFactor[4];
  if(m_State >= WRITING)
  {
    blendFactor[0] = blendConst[0];
    blendFactor[1] = blendConst[1];
    blendFactor[2] = blendConst[2];
    blendFactor[3] = blendConst[3];
  }
  localSerialiser->SerialisePODArray<4>("blendConst", blendFactor);

  Serialise_DebugMessages(localSerialiser, false);

  if(m_State < WRITING)
    m_LastCmdBufferID = cmdid;

  if(m_State == EXECUTING)
  {
    if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
    {
      cmdBuffer = RerecordCmdBuf(cmdid);
      ObjDisp(cmdBuffer)->CmdSetBlendConstants(Unwrap(cmdBuffer), blendFactor);
      memcpy(m_RenderState.blendConst, blendFactor, sizeof(blendFactor));
    }
  }
  else if(m_State == READING)
  {
    cmdBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);

    ObjDisp(cmdBuffer)->CmdSetBlendConstants(Unwrap(cmdBuffer), blendFactor);
  }

  return true;
}