blob: 056d71dc8fd80f7148ccb8042f1e67c8a5bff4aa [file] [log] [blame]
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/vk/GrVkCommandBuffer.h"
#include "include/core/SkRect.h"
#include "src/core/SkTraceEvent.h"
#include "src/gpu/vk/GrVkBuffer.h"
#include "src/gpu/vk/GrVkCommandPool.h"
#include "src/gpu/vk/GrVkFramebuffer.h"
#include "src/gpu/vk/GrVkGpu.h"
#include "src/gpu/vk/GrVkImage.h"
#include "src/gpu/vk/GrVkImageView.h"
#include "src/gpu/vk/GrVkPipeline.h"
#include "src/gpu/vk/GrVkPipelineState.h"
#include "src/gpu/vk/GrVkPipelineState.h"
#include "src/gpu/vk/GrVkRenderPass.h"
#include "src/gpu/vk/GrVkRenderTarget.h"
#include "src/gpu/vk/GrVkUtil.h"
void GrVkCommandBuffer::invalidateState() {
for (auto& boundInputBuffer : fBoundInputBuffers) {
boundInputBuffer = VK_NULL_HANDLE;
}
fBoundIndexBuffer = VK_NULL_HANDLE;
memset(&fCachedViewport, 0, sizeof(VkViewport));
fCachedViewport.width = - 1.0f; // Viewport must have a width greater than 0
memset(&fCachedScissor, 0, sizeof(VkRect2D));
fCachedScissor.offset.x = -1; // Scissor offset must be greater that 0 to be valid
for (int i = 0; i < 4; ++i) {
fCachedBlendConstant[i] = -1.0;
}
}
void GrVkCommandBuffer::freeGPUData(const GrGpu* gpu, VkCommandPool cmdPool) const {
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
SkASSERT(!fIsActive);
SkASSERT(!fTrackedResources.count());
SkASSERT(!fTrackedRecycledResources.count());
SkASSERT(!fTrackedGpuBuffers.count());
SkASSERT(!fTrackedGpuSurfaces.count());
SkASSERT(cmdPool != VK_NULL_HANDLE);
SkASSERT(!this->isWrapped());
GrVkGpu* vkGpu = (GrVkGpu*)gpu;
GR_VK_CALL(vkGpu->vkInterface(), FreeCommandBuffers(vkGpu->device(), cmdPool, 1, &fCmdBuffer));
this->onFreeGPUData(vkGpu);
}
void GrVkCommandBuffer::releaseResources() {
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
SkASSERT(!fIsActive || this->isWrapped());
fTrackedResources.reset();
fTrackedRecycledResources.reset();
fTrackedGpuBuffers.reset();
fTrackedGpuSurfaces.reset();
this->invalidateState();
this->onReleaseResources();
}
////////////////////////////////////////////////////////////////////////////////
// CommandBuffer commands
////////////////////////////////////////////////////////////////////////////////
void GrVkCommandBuffer::pipelineBarrier(const GrVkGpu* gpu,
const GrManagedResource* resource,
VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
bool byRegion,
BarrierType barrierType,
void* barrier) {
SkASSERT(!this->isWrapped());
SkASSERT(fIsActive);
#ifdef SK_DEBUG
// For images we can have barriers inside of render passes but they require us to add more
// support in subpasses which need self dependencies to have barriers inside them. Also, we can
// never have buffer barriers inside of a render pass. For now we will just assert that we are
// not in a render pass.
bool isValidSubpassBarrier = false;
if (barrierType == kImageMemory_BarrierType) {
VkImageMemoryBarrier* imgBarrier = static_cast<VkImageMemoryBarrier*>(barrier);
isValidSubpassBarrier = (imgBarrier->newLayout == imgBarrier->oldLayout) &&
(imgBarrier->srcQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) &&
(imgBarrier->dstQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) &&
byRegion;
}
SkASSERT(!fActiveRenderPass || isValidSubpassBarrier);
#endif
if (barrierType == kBufferMemory_BarrierType) {
const VkBufferMemoryBarrier* barrierPtr = static_cast<VkBufferMemoryBarrier*>(barrier);
fBufferBarriers.push_back(*barrierPtr);
} else {
SkASSERT(barrierType == kImageMemory_BarrierType);
const VkImageMemoryBarrier* barrierPtr = static_cast<VkImageMemoryBarrier*>(barrier);
// We need to check if we are adding a pipeline barrier that covers part of the same
// subresource range as a barrier that is already in current batch. If it does, then we must
// submit the first batch because the vulkan spec does not define a specific ordering for
// barriers submitted in the same batch.
// TODO: Look if we can gain anything by merging barriers together instead of submitting
// the old ones.
for (int i = 0; i < fImageBarriers.count(); ++i) {
VkImageMemoryBarrier& currentBarrier = fImageBarriers[i];
if (barrierPtr->image == currentBarrier.image) {
const VkImageSubresourceRange newRange = barrierPtr->subresourceRange;
const VkImageSubresourceRange oldRange = currentBarrier.subresourceRange;
SkASSERT(newRange.aspectMask == oldRange.aspectMask);
SkASSERT(newRange.baseArrayLayer == oldRange.baseArrayLayer);
SkASSERT(newRange.layerCount == oldRange.layerCount);
uint32_t newStart = newRange.baseMipLevel;
uint32_t newEnd = newRange.baseMipLevel + newRange.levelCount - 1;
uint32_t oldStart = oldRange.baseMipLevel;
uint32_t oldEnd = oldRange.baseMipLevel + oldRange.levelCount - 1;
if (std::max(newStart, oldStart) <= std::min(newEnd, oldEnd)) {
this->submitPipelineBarriers(gpu);
break;
}
}
}
fImageBarriers.push_back(*barrierPtr);
}
fBarriersByRegion |= byRegion;
fSrcStageMask = fSrcStageMask | srcStageMask;
fDstStageMask = fDstStageMask | dstStageMask;
fHasWork = true;
if (resource) {
this->addResource(resource);
}
if (fActiveRenderPass) {
this->submitPipelineBarriers(gpu, true);
}
}
void GrVkCommandBuffer::submitPipelineBarriers(const GrVkGpu* gpu, bool forSelfDependency) {
SkASSERT(fIsActive);
// Currently we never submit a pipeline barrier without at least one memory barrier.
if (fBufferBarriers.count() || fImageBarriers.count()) {
// For images we can have barriers inside of render passes but they require us to add more
// support in subpasses which need self dependencies to have barriers inside them. Also, we
// can never have buffer barriers inside of a render pass. For now we will just assert that
// we are not in a render pass.
SkASSERT(!fActiveRenderPass || forSelfDependency);
SkASSERT(!this->isWrapped());
SkASSERT(fSrcStageMask && fDstStageMask);
VkDependencyFlags dependencyFlags = fBarriersByRegion ? VK_DEPENDENCY_BY_REGION_BIT : 0;
GR_VK_CALL(gpu->vkInterface(), CmdPipelineBarrier(
fCmdBuffer, fSrcStageMask, fDstStageMask, dependencyFlags, 0, nullptr,
fBufferBarriers.count(), fBufferBarriers.begin(),
fImageBarriers.count(), fImageBarriers.begin()));
fBufferBarriers.reset();
fImageBarriers.reset();
fBarriersByRegion = false;
fSrcStageMask = 0;
fDstStageMask = 0;
}
SkASSERT(!fBufferBarriers.count());
SkASSERT(!fImageBarriers.count());
SkASSERT(!fBarriersByRegion);
SkASSERT(!fSrcStageMask);
SkASSERT(!fDstStageMask);
}
void GrVkCommandBuffer::bindInputBuffer(GrVkGpu* gpu, uint32_t binding,
sk_sp<const GrBuffer> buffer) {
VkBuffer vkBuffer = static_cast<const GrVkBuffer*>(buffer.get())->vkBuffer();
SkASSERT(VK_NULL_HANDLE != vkBuffer);
SkASSERT(binding < kMaxInputBuffers);
// TODO: once vbuffer->offset() no longer always returns 0, we will need to track the offset
// to know if we can skip binding or not.
if (vkBuffer != fBoundInputBuffers[binding]) {
VkDeviceSize offset = 0;
GR_VK_CALL(gpu->vkInterface(), CmdBindVertexBuffers(fCmdBuffer,
binding,
1,
&vkBuffer,
&offset));
fBoundInputBuffers[binding] = vkBuffer;
this->addGrBuffer(std::move(buffer));
}
}
void GrVkCommandBuffer::bindIndexBuffer(GrVkGpu* gpu, sk_sp<const GrBuffer> buffer) {
VkBuffer vkBuffer = static_cast<const GrVkBuffer*>(buffer.get())->vkBuffer();
SkASSERT(VK_NULL_HANDLE != vkBuffer);
// TODO: once ibuffer->offset() no longer always returns 0, we will need to track the offset
// to know if we can skip binding or not.
if (vkBuffer != fBoundIndexBuffer) {
GR_VK_CALL(gpu->vkInterface(), CmdBindIndexBuffer(fCmdBuffer,
vkBuffer, /*offset=*/0,
VK_INDEX_TYPE_UINT16));
fBoundIndexBuffer = vkBuffer;
this->addGrBuffer(std::move(buffer));
}
}
void GrVkCommandBuffer::clearAttachments(const GrVkGpu* gpu,
int numAttachments,
const VkClearAttachment* attachments,
int numRects,
const VkClearRect* clearRects) {
SkASSERT(fIsActive);
SkASSERT(fActiveRenderPass);
SkASSERT(numAttachments > 0);
SkASSERT(numRects > 0);
this->addingWork(gpu);
#ifdef SK_DEBUG
for (int i = 0; i < numAttachments; ++i) {
if (attachments[i].aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) {
uint32_t testIndex;
SkAssertResult(fActiveRenderPass->colorAttachmentIndex(&testIndex));
SkASSERT(testIndex == attachments[i].colorAttachment);
}
}
#endif
GR_VK_CALL(gpu->vkInterface(), CmdClearAttachments(fCmdBuffer,
numAttachments,
attachments,
numRects,
clearRects));
if (gpu->vkCaps().mustInvalidatePrimaryCmdBufferStateAfterClearAttachments()) {
this->invalidateState();
}
}
void GrVkCommandBuffer::bindDescriptorSets(const GrVkGpu* gpu,
VkPipelineLayout layout,
uint32_t firstSet,
uint32_t setCount,
const VkDescriptorSet* descriptorSets,
uint32_t dynamicOffsetCount,
const uint32_t* dynamicOffsets) {
SkASSERT(fIsActive);
GR_VK_CALL(gpu->vkInterface(), CmdBindDescriptorSets(fCmdBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
layout,
firstSet,
setCount,
descriptorSets,
dynamicOffsetCount,
dynamicOffsets));
}
void GrVkCommandBuffer::bindPipeline(const GrVkGpu* gpu, sk_sp<const GrVkPipeline> pipeline) {
SkASSERT(fIsActive);
GR_VK_CALL(gpu->vkInterface(), CmdBindPipeline(fCmdBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline->pipeline()));
this->addResource(std::move(pipeline));
}
void GrVkCommandBuffer::pushConstants(const GrVkGpu* gpu, VkPipelineLayout layout,
VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size,
const void* values) {
SkASSERT(fIsActive);
// offset and size must be a multiple of 4
SkASSERT(!SkToBool(offset & 0x3));
SkASSERT(!SkToBool(size & 0x3));
GR_VK_CALL(gpu->vkInterface(), CmdPushConstants(fCmdBuffer,
layout,
stageFlags,
offset,
size,
values));
}
void GrVkCommandBuffer::drawIndexed(const GrVkGpu* gpu,
uint32_t indexCount,
uint32_t instanceCount,
uint32_t firstIndex,
int32_t vertexOffset,
uint32_t firstInstance) {
SkASSERT(fIsActive);
SkASSERT(fActiveRenderPass);
this->addingWork(gpu);
GR_VK_CALL(gpu->vkInterface(), CmdDrawIndexed(fCmdBuffer,
indexCount,
instanceCount,
firstIndex,
vertexOffset,
firstInstance));
}
void GrVkCommandBuffer::draw(const GrVkGpu* gpu,
uint32_t vertexCount,
uint32_t instanceCount,
uint32_t firstVertex,
uint32_t firstInstance) {
SkASSERT(fIsActive);
SkASSERT(fActiveRenderPass);
this->addingWork(gpu);
GR_VK_CALL(gpu->vkInterface(), CmdDraw(fCmdBuffer,
vertexCount,
instanceCount,
firstVertex,
firstInstance));
}
void GrVkCommandBuffer::drawIndirect(const GrVkGpu* gpu,
sk_sp<const GrBuffer> indirectBuffer,
VkDeviceSize offset,
uint32_t drawCount,
uint32_t stride) {
SkASSERT(fIsActive);
SkASSERT(fActiveRenderPass);
SkASSERT(!indirectBuffer->isCpuBuffer());
this->addingWork(gpu);
VkBuffer vkBuffer = static_cast<const GrVkBuffer*>(indirectBuffer.get())->vkBuffer();
GR_VK_CALL(gpu->vkInterface(), CmdDrawIndirect(fCmdBuffer,
vkBuffer,
offset,
drawCount,
stride));
this->addGrBuffer(std::move(indirectBuffer));
}
void GrVkCommandBuffer::drawIndexedIndirect(const GrVkGpu* gpu,
sk_sp<const GrBuffer> indirectBuffer,
VkDeviceSize offset,
uint32_t drawCount,
uint32_t stride) {
SkASSERT(fIsActive);
SkASSERT(fActiveRenderPass);
SkASSERT(!indirectBuffer->isCpuBuffer());
this->addingWork(gpu);
VkBuffer vkBuffer = static_cast<const GrVkBuffer*>(indirectBuffer.get())->vkBuffer();
GR_VK_CALL(gpu->vkInterface(), CmdDrawIndexedIndirect(fCmdBuffer,
vkBuffer,
offset,
drawCount,
stride));
this->addGrBuffer(std::move(indirectBuffer));
}
void GrVkCommandBuffer::setViewport(const GrVkGpu* gpu,
uint32_t firstViewport,
uint32_t viewportCount,
const VkViewport* viewports) {
SkASSERT(fIsActive);
SkASSERT(1 == viewportCount);
if (0 != memcmp(viewports, &fCachedViewport, sizeof(VkViewport))) {
GR_VK_CALL(gpu->vkInterface(), CmdSetViewport(fCmdBuffer,
firstViewport,
viewportCount,
viewports));
fCachedViewport = viewports[0];
}
}
void GrVkCommandBuffer::setScissor(const GrVkGpu* gpu,
uint32_t firstScissor,
uint32_t scissorCount,
const VkRect2D* scissors) {
SkASSERT(fIsActive);
SkASSERT(1 == scissorCount);
if (0 != memcmp(scissors, &fCachedScissor, sizeof(VkRect2D))) {
GR_VK_CALL(gpu->vkInterface(), CmdSetScissor(fCmdBuffer,
firstScissor,
scissorCount,
scissors));
fCachedScissor = scissors[0];
}
}
void GrVkCommandBuffer::setBlendConstants(const GrVkGpu* gpu,
const float blendConstants[4]) {
SkASSERT(fIsActive);
if (0 != memcmp(blendConstants, fCachedBlendConstant, 4 * sizeof(float))) {
GR_VK_CALL(gpu->vkInterface(), CmdSetBlendConstants(fCmdBuffer, blendConstants));
memcpy(fCachedBlendConstant, blendConstants, 4 * sizeof(float));
}
}
void GrVkCommandBuffer::addingWork(const GrVkGpu* gpu) {
this->submitPipelineBarriers(gpu);
fHasWork = true;
}
///////////////////////////////////////////////////////////////////////////////
// PrimaryCommandBuffer
////////////////////////////////////////////////////////////////////////////////
GrVkPrimaryCommandBuffer::~GrVkPrimaryCommandBuffer() {
// Should have ended any render pass we're in the middle of
SkASSERT(!fActiveRenderPass);
}
GrVkPrimaryCommandBuffer* GrVkPrimaryCommandBuffer::Create(GrVkGpu* gpu,
VkCommandPool cmdPool) {
const VkCommandBufferAllocateInfo cmdInfo = {
VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // sType
nullptr, // pNext
cmdPool, // commandPool
VK_COMMAND_BUFFER_LEVEL_PRIMARY, // level
1 // bufferCount
};
VkCommandBuffer cmdBuffer;
VkResult err;
GR_VK_CALL_RESULT(gpu, err, AllocateCommandBuffers(gpu->device(), &cmdInfo, &cmdBuffer));
if (err) {
return nullptr;
}
return new GrVkPrimaryCommandBuffer(cmdBuffer);
}
void GrVkPrimaryCommandBuffer::begin(GrVkGpu* gpu) {
SkASSERT(!fIsActive);
VkCommandBufferBeginInfo cmdBufferBeginInfo;
memset(&cmdBufferBeginInfo, 0, sizeof(VkCommandBufferBeginInfo));
cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
cmdBufferBeginInfo.pNext = nullptr;
cmdBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
cmdBufferBeginInfo.pInheritanceInfo = nullptr;
GR_VK_CALL_ERRCHECK(gpu, BeginCommandBuffer(fCmdBuffer, &cmdBufferBeginInfo));
fIsActive = true;
}
void GrVkPrimaryCommandBuffer::end(GrVkGpu* gpu, bool abandoningBuffer) {
SkASSERT(fIsActive);
SkASSERT(!fActiveRenderPass);
// If we are in the process of abandoning the context then the GrResourceCache will have freed
// all resources before destroying the GrVkGpu. When we destroy the GrVkGpu we call end on the
// command buffer to keep all our state tracking consistent. However, the vulkan validation
// layers complain about calling end on a command buffer that contains resources that have
// already been deleted. From the vulkan API it isn't required to end the command buffer to
// delete it, so we just skip the vulkan API calls and update our own state tracking.
if (!abandoningBuffer) {
this->submitPipelineBarriers(gpu);
GR_VK_CALL_ERRCHECK(gpu, EndCommandBuffer(fCmdBuffer));
}
this->invalidateState();
fIsActive = false;
fHasWork = false;
}
bool GrVkPrimaryCommandBuffer::beginRenderPass(GrVkGpu* gpu,
const GrVkRenderPass* renderPass,
sk_sp<const GrVkFramebuffer> framebuffer,
const VkClearValue clearValues[],
const GrSurface* target,
const SkIRect& bounds,
bool forSecondaryCB) {
SkASSERT(fIsActive);
SkASSERT(!fActiveRenderPass);
SkASSERT(framebuffer);
this->addingWork(gpu);
VkRenderPassBeginInfo beginInfo;
VkRect2D renderArea;
renderArea.offset = { bounds.fLeft , bounds.fTop };
renderArea.extent = { (uint32_t)bounds.width(), (uint32_t)bounds.height() };
memset(&beginInfo, 0, sizeof(VkRenderPassBeginInfo));
beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
beginInfo.pNext = nullptr;
beginInfo.renderPass = renderPass->vkRenderPass();
beginInfo.framebuffer = framebuffer->framebuffer();
beginInfo.renderArea = renderArea;
beginInfo.clearValueCount = renderPass->clearValueCount();
beginInfo.pClearValues = clearValues;
VkSubpassContents contents = forSecondaryCB ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS
: VK_SUBPASS_CONTENTS_INLINE;
GR_VK_CALL(gpu->vkInterface(), CmdBeginRenderPass(fCmdBuffer, &beginInfo, contents));
fActiveRenderPass = renderPass;
this->addResource(renderPass);
this->addResource(std::move(framebuffer));
this->addGrSurface(sk_ref_sp(target));
return true;
}
void GrVkPrimaryCommandBuffer::endRenderPass(const GrVkGpu* gpu) {
SkASSERT(fIsActive);
SkASSERT(fActiveRenderPass);
this->addingWork(gpu);
GR_VK_CALL(gpu->vkInterface(), CmdEndRenderPass(fCmdBuffer));
fActiveRenderPass = nullptr;
}
void GrVkPrimaryCommandBuffer::nexSubpass(GrVkGpu* gpu, bool forSecondaryCB) {
SkASSERT(fIsActive);
SkASSERT(fActiveRenderPass);
VkSubpassContents contents = forSecondaryCB ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS
: VK_SUBPASS_CONTENTS_INLINE;
GR_VK_CALL(gpu->vkInterface(), CmdNextSubpass(fCmdBuffer, contents));
}
void GrVkPrimaryCommandBuffer::executeCommands(const GrVkGpu* gpu,
std::unique_ptr<GrVkSecondaryCommandBuffer> buffer) {
// The Vulkan spec allows secondary command buffers to be executed on a primary command buffer
// if the command pools both were created from were created with the same queue family. However,
// we currently always create them from the same pool.
SkASSERT(fIsActive);
SkASSERT(!buffer->fIsActive);
SkASSERT(fActiveRenderPass);
SkASSERT(fActiveRenderPass->isCompatible(*buffer->fActiveRenderPass));
this->addingWork(gpu);
GR_VK_CALL(gpu->vkInterface(), CmdExecuteCommands(fCmdBuffer, 1, &buffer->fCmdBuffer));
fSecondaryCommandBuffers.push_back(std::move(buffer));
// When executing a secondary command buffer all state (besides render pass state) becomes
// invalidated and must be reset. This includes bound buffers, pipelines, dynamic state, etc.
this->invalidateState();
}
static bool submit_to_queue(GrVkGpu* gpu,
VkQueue queue,
VkFence fence,
uint32_t waitCount,
const VkSemaphore* waitSemaphores,
const VkPipelineStageFlags* waitStages,
uint32_t commandBufferCount,
const VkCommandBuffer* commandBuffers,
uint32_t signalCount,
const VkSemaphore* signalSemaphores,
GrProtected protectedContext) {
VkProtectedSubmitInfo protectedSubmitInfo;
if (protectedContext == GrProtected::kYes) {
memset(&protectedSubmitInfo, 0, sizeof(VkProtectedSubmitInfo));
protectedSubmitInfo.sType = VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO;
protectedSubmitInfo.pNext = nullptr;
protectedSubmitInfo.protectedSubmit = VK_TRUE;
}
VkSubmitInfo submitInfo;
memset(&submitInfo, 0, sizeof(VkSubmitInfo));
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.pNext = protectedContext == GrProtected::kYes ? &protectedSubmitInfo : nullptr;
submitInfo.waitSemaphoreCount = waitCount;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
submitInfo.commandBufferCount = commandBufferCount;
submitInfo.pCommandBuffers = commandBuffers;
submitInfo.signalSemaphoreCount = signalCount;
submitInfo.pSignalSemaphores = signalSemaphores;
VkResult result;
GR_VK_CALL_RESULT(gpu, result, QueueSubmit(queue, 1, &submitInfo, fence));
return result == VK_SUCCESS;
}
bool GrVkPrimaryCommandBuffer::submitToQueue(
GrVkGpu* gpu,
VkQueue queue,
SkTArray<GrVkSemaphore::Resource*>& signalSemaphores,
SkTArray<GrVkSemaphore::Resource*>& waitSemaphores) {
SkASSERT(!fIsActive);
VkResult err;
if (VK_NULL_HANDLE == fSubmitFence) {
VkFenceCreateInfo fenceInfo;
memset(&fenceInfo, 0, sizeof(VkFenceCreateInfo));
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
GR_VK_CALL_RESULT(gpu, err, CreateFence(gpu->device(), &fenceInfo, nullptr,
&fSubmitFence));
if (err) {
fSubmitFence = VK_NULL_HANDLE;
return false;
}
} else {
// This cannot return DEVICE_LOST so we assert we succeeded.
GR_VK_CALL_RESULT(gpu, err, ResetFences(gpu->device(), 1, &fSubmitFence));
SkASSERT(err == VK_SUCCESS);
}
int signalCount = signalSemaphores.count();
int waitCount = waitSemaphores.count();
bool submitted = false;
if (0 == signalCount && 0 == waitCount) {
// This command buffer has no dependent semaphores so we can simply just submit it to the
// queue with no worries.
submitted = submit_to_queue(
gpu, queue, fSubmitFence, 0, nullptr, nullptr, 1, &fCmdBuffer, 0, nullptr,
gpu->protectedContext() ? GrProtected::kYes : GrProtected::kNo);
} else {
SkTArray<VkSemaphore> vkSignalSems(signalCount);
for (int i = 0; i < signalCount; ++i) {
if (signalSemaphores[i]->shouldSignal()) {
this->addResource(signalSemaphores[i]);
vkSignalSems.push_back(signalSemaphores[i]->semaphore());
}
}
SkTArray<VkSemaphore> vkWaitSems(waitCount);
SkTArray<VkPipelineStageFlags> vkWaitStages(waitCount);
for (int i = 0; i < waitCount; ++i) {
if (waitSemaphores[i]->shouldWait()) {
this->addResource(waitSemaphores[i]);
vkWaitSems.push_back(waitSemaphores[i]->semaphore());
vkWaitStages.push_back(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
}
}
submitted = submit_to_queue(gpu, queue, fSubmitFence, vkWaitSems.count(),
vkWaitSems.begin(), vkWaitStages.begin(), 1, &fCmdBuffer,
vkSignalSems.count(), vkSignalSems.begin(),
gpu->protectedContext() ? GrProtected::kYes : GrProtected::kNo);
if (submitted) {
for (int i = 0; i < signalCount; ++i) {
signalSemaphores[i]->markAsSignaled();
}
for (int i = 0; i < waitCount; ++i) {
waitSemaphores[i]->markAsWaited();
}
}
}
if (!submitted) {
// Destroy the fence or else we will try to wait forever for it to finish.
GR_VK_CALL(gpu->vkInterface(), DestroyFence(gpu->device(), fSubmitFence, nullptr));
fSubmitFence = VK_NULL_HANDLE;
return false;
}
return true;
}
void GrVkPrimaryCommandBuffer::forceSync(GrVkGpu* gpu) {
if (fSubmitFence == VK_NULL_HANDLE) {
return;
}
GR_VK_CALL_ERRCHECK(gpu, WaitForFences(gpu->device(), 1, &fSubmitFence, true, UINT64_MAX));
}
bool GrVkPrimaryCommandBuffer::finished(GrVkGpu* gpu) {
SkASSERT(!fIsActive);
if (VK_NULL_HANDLE == fSubmitFence) {
return true;
}
VkResult err;
GR_VK_CALL_RESULT_NOCHECK(gpu, err, GetFenceStatus(gpu->device(), fSubmitFence));
switch (err) {
case VK_SUCCESS:
case VK_ERROR_DEVICE_LOST:
return true;
case VK_NOT_READY:
return false;
default:
SkDebugf("Error getting fence status: %d\n", err);
SK_ABORT("Got an invalid fence status");
return false;
}
}
void GrVkPrimaryCommandBuffer::addFinishedProc(sk_sp<GrRefCntedCallback> finishedProc) {
fFinishedProcs.push_back(std::move(finishedProc));
}
void GrVkPrimaryCommandBuffer::onReleaseResources() {
for (int i = 0; i < fSecondaryCommandBuffers.count(); ++i) {
fSecondaryCommandBuffers[i]->releaseResources();
}
this->callFinishedProcs();
}
void GrVkPrimaryCommandBuffer::recycleSecondaryCommandBuffers(GrVkCommandPool* cmdPool) {
for (int i = 0; i < fSecondaryCommandBuffers.count(); ++i) {
fSecondaryCommandBuffers[i].release()->recycle(cmdPool);
}
fSecondaryCommandBuffers.reset();
}
void GrVkPrimaryCommandBuffer::copyImage(const GrVkGpu* gpu,
GrVkImage* srcImage,
VkImageLayout srcLayout,
GrVkImage* dstImage,
VkImageLayout dstLayout,
uint32_t copyRegionCount,
const VkImageCopy* copyRegions) {
SkASSERT(fIsActive);
SkASSERT(!fActiveRenderPass);
this->addingWork(gpu);
this->addResource(srcImage->resource());
this->addResource(dstImage->resource());
GR_VK_CALL(gpu->vkInterface(), CmdCopyImage(fCmdBuffer,
srcImage->image(),
srcLayout,
dstImage->image(),
dstLayout,
copyRegionCount,
copyRegions));
}
void GrVkPrimaryCommandBuffer::blitImage(const GrVkGpu* gpu,
const GrManagedResource* srcResource,
VkImage srcImage,
VkImageLayout srcLayout,
const GrManagedResource* dstResource,
VkImage dstImage,
VkImageLayout dstLayout,
uint32_t blitRegionCount,
const VkImageBlit* blitRegions,
VkFilter filter) {
SkASSERT(fIsActive);
SkASSERT(!fActiveRenderPass);
this->addingWork(gpu);
this->addResource(srcResource);
this->addResource(dstResource);
GR_VK_CALL(gpu->vkInterface(), CmdBlitImage(fCmdBuffer,
srcImage,
srcLayout,
dstImage,
dstLayout,
blitRegionCount,
blitRegions,
filter));
}
void GrVkPrimaryCommandBuffer::blitImage(const GrVkGpu* gpu,
const GrVkImage& srcImage,
const GrVkImage& dstImage,
uint32_t blitRegionCount,
const VkImageBlit* blitRegions,
VkFilter filter) {
this->blitImage(gpu,
srcImage.resource(),
srcImage.image(),
srcImage.currentLayout(),
dstImage.resource(),
dstImage.image(),
dstImage.currentLayout(),
blitRegionCount,
blitRegions,
filter);
}
void GrVkPrimaryCommandBuffer::copyImageToBuffer(const GrVkGpu* gpu,
GrVkImage* srcImage,
VkImageLayout srcLayout,
sk_sp<GrGpuBuffer> dstBuffer,
uint32_t copyRegionCount,
const VkBufferImageCopy* copyRegions) {
SkASSERT(fIsActive);
SkASSERT(!fActiveRenderPass);
this->addingWork(gpu);
GrVkBuffer* vkBuffer = static_cast<GrVkBuffer*>(dstBuffer.get());
GR_VK_CALL(gpu->vkInterface(), CmdCopyImageToBuffer(fCmdBuffer,
srcImage->image(),
srcLayout,
vkBuffer->vkBuffer(),
copyRegionCount,
copyRegions));
this->addResource(srcImage->resource());
this->addGrBuffer(std::move(dstBuffer));
}
void GrVkPrimaryCommandBuffer::copyBufferToImage(const GrVkGpu* gpu,
VkBuffer srcBuffer,
GrVkImage* dstImage,
VkImageLayout dstLayout,
uint32_t copyRegionCount,
const VkBufferImageCopy* copyRegions) {
SkASSERT(fIsActive);
SkASSERT(!fActiveRenderPass);
this->addingWork(gpu);
GR_VK_CALL(gpu->vkInterface(), CmdCopyBufferToImage(fCmdBuffer,
srcBuffer,
dstImage->image(),
dstLayout,
copyRegionCount,
copyRegions));
this->addResource(dstImage->resource());
}
void GrVkPrimaryCommandBuffer::copyBuffer(GrVkGpu* gpu,
sk_sp<GrGpuBuffer> srcBuffer,
sk_sp<GrGpuBuffer> dstBuffer,
uint32_t regionCount,
const VkBufferCopy* regions) {
SkASSERT(fIsActive);
SkASSERT(!fActiveRenderPass);
this->addingWork(gpu);
#ifdef SK_DEBUG
for (uint32_t i = 0; i < regionCount; ++i) {
const VkBufferCopy& region = regions[i];
SkASSERT(region.size > 0);
SkASSERT(region.srcOffset < srcBuffer->size());
SkASSERT(region.dstOffset < dstBuffer->size());
SkASSERT(region.srcOffset + region.size <= srcBuffer->size());
SkASSERT(region.dstOffset + region.size <= dstBuffer->size());
}
#endif
const GrVkBuffer* srcVk = static_cast<GrVkBuffer*>(srcBuffer.get());
const GrVkBuffer* dstVk = static_cast<GrVkBuffer*>(dstBuffer.get());
GR_VK_CALL(gpu->vkInterface(), CmdCopyBuffer(fCmdBuffer,
srcVk->vkBuffer(),
dstVk->vkBuffer(),
regionCount,
regions));
this->addGrBuffer(std::move(srcBuffer));
this->addGrBuffer(std::move(dstBuffer));
}
void GrVkPrimaryCommandBuffer::updateBuffer(GrVkGpu* gpu,
sk_sp<GrVkBuffer> dstBuffer,
VkDeviceSize dstOffset,
VkDeviceSize dataSize,
const void* data) {
SkASSERT(fIsActive);
SkASSERT(!fActiveRenderPass);
SkASSERT(0 == (dstOffset & 0x03)); // four byte aligned
// TODO: handle larger transfer sizes
SkASSERT(dataSize <= 65536);
SkASSERT(0 == (dataSize & 0x03)); // four byte aligned
this->addingWork(gpu);
GR_VK_CALL(
gpu->vkInterface(),
CmdUpdateBuffer(
fCmdBuffer, dstBuffer->vkBuffer(), dstOffset, dataSize, (const uint32_t*)data));
this->addGrBuffer(std::move(dstBuffer));
}
void GrVkPrimaryCommandBuffer::clearColorImage(const GrVkGpu* gpu,
GrVkImage* image,
const VkClearColorValue* color,
uint32_t subRangeCount,
const VkImageSubresourceRange* subRanges) {
SkASSERT(fIsActive);
SkASSERT(!fActiveRenderPass);
this->addingWork(gpu);
this->addResource(image->resource());
GR_VK_CALL(gpu->vkInterface(), CmdClearColorImage(fCmdBuffer,
image->image(),
image->currentLayout(),
color,
subRangeCount,
subRanges));
}
void GrVkPrimaryCommandBuffer::clearDepthStencilImage(const GrVkGpu* gpu,
GrVkImage* image,
const VkClearDepthStencilValue* color,
uint32_t subRangeCount,
const VkImageSubresourceRange* subRanges) {
SkASSERT(fIsActive);
SkASSERT(!fActiveRenderPass);
this->addingWork(gpu);
this->addResource(image->resource());
GR_VK_CALL(gpu->vkInterface(), CmdClearDepthStencilImage(fCmdBuffer,
image->image(),
image->currentLayout(),
color,
subRangeCount,
subRanges));
}
void GrVkPrimaryCommandBuffer::resolveImage(GrVkGpu* gpu,
const GrVkImage& srcImage,
const GrVkImage& dstImage,
uint32_t regionCount,
const VkImageResolve* regions) {
SkASSERT(fIsActive);
SkASSERT(!fActiveRenderPass);
this->addingWork(gpu);
this->addResource(srcImage.resource());
this->addResource(dstImage.resource());
GR_VK_CALL(gpu->vkInterface(), CmdResolveImage(fCmdBuffer,
srcImage.image(),
srcImage.currentLayout(),
dstImage.image(),
dstImage.currentLayout(),
regionCount,
regions));
}
void GrVkPrimaryCommandBuffer::onFreeGPUData(const GrVkGpu* gpu) const {
SkASSERT(!fActiveRenderPass);
// Destroy the fence, if any
if (VK_NULL_HANDLE != fSubmitFence) {
GR_VK_CALL(gpu->vkInterface(), DestroyFence(gpu->device(), fSubmitFence, nullptr));
}
SkASSERT(!fSecondaryCommandBuffers.count());
}
///////////////////////////////////////////////////////////////////////////////
// SecondaryCommandBuffer
////////////////////////////////////////////////////////////////////////////////
GrVkSecondaryCommandBuffer* GrVkSecondaryCommandBuffer::Create(GrVkGpu* gpu,
GrVkCommandPool* cmdPool) {
SkASSERT(cmdPool);
const VkCommandBufferAllocateInfo cmdInfo = {
VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // sType
nullptr, // pNext
cmdPool->vkCommandPool(), // commandPool
VK_COMMAND_BUFFER_LEVEL_SECONDARY, // level
1 // bufferCount
};
VkCommandBuffer cmdBuffer;
VkResult err;
GR_VK_CALL_RESULT(gpu, err, AllocateCommandBuffers(gpu->device(), &cmdInfo, &cmdBuffer));
if (err) {
return nullptr;
}
return new GrVkSecondaryCommandBuffer(cmdBuffer, /*externalRenderPass=*/nullptr);
}
GrVkSecondaryCommandBuffer* GrVkSecondaryCommandBuffer::Create(
VkCommandBuffer cmdBuffer, const GrVkRenderPass* externalRenderPass) {
return new GrVkSecondaryCommandBuffer(cmdBuffer, externalRenderPass);
}
void GrVkSecondaryCommandBuffer::begin(GrVkGpu* gpu, const GrVkFramebuffer* framebuffer,
const GrVkRenderPass* compatibleRenderPass) {
SkASSERT(!fIsActive);
SkASSERT(!this->isWrapped());
SkASSERT(compatibleRenderPass);
fActiveRenderPass = compatibleRenderPass;
VkCommandBufferInheritanceInfo inheritanceInfo;
memset(&inheritanceInfo, 0, sizeof(VkCommandBufferInheritanceInfo));
inheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
inheritanceInfo.pNext = nullptr;
inheritanceInfo.renderPass = fActiveRenderPass->vkRenderPass();
inheritanceInfo.subpass = 0; // Currently only using 1 subpass for each render pass
inheritanceInfo.framebuffer = framebuffer ? framebuffer->framebuffer() : VK_NULL_HANDLE;
inheritanceInfo.occlusionQueryEnable = false;
inheritanceInfo.queryFlags = 0;
inheritanceInfo.pipelineStatistics = 0;
VkCommandBufferBeginInfo cmdBufferBeginInfo;
memset(&cmdBufferBeginInfo, 0, sizeof(VkCommandBufferBeginInfo));
cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
cmdBufferBeginInfo.pNext = nullptr;
cmdBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT |
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
cmdBufferBeginInfo.pInheritanceInfo = &inheritanceInfo;
GR_VK_CALL_ERRCHECK(gpu, BeginCommandBuffer(fCmdBuffer, &cmdBufferBeginInfo));
fIsActive = true;
}
void GrVkSecondaryCommandBuffer::end(GrVkGpu* gpu) {
SkASSERT(fIsActive);
SkASSERT(!this->isWrapped());
GR_VK_CALL_ERRCHECK(gpu, EndCommandBuffer(fCmdBuffer));
this->invalidateState();
fHasWork = false;
fIsActive = false;
}
void GrVkSecondaryCommandBuffer::recycle(GrVkCommandPool* cmdPool) {
if (this->isWrapped()) {
delete this;
} else {
cmdPool->recycleSecondaryCommandBuffer(this);
}
}