| // |
| // Copyright 2021 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // CLMemoryVk.cpp: Implements the class methods for CLMemoryVk. |
| // |
| |
| #ifdef UNSAFE_BUFFERS_BUILD |
| # pragma allow_unsafe_buffers |
| #endif |
| |
| #include "common/log_utils.h" |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include "libANGLE/renderer/vulkan/CLContextVk.h" |
| #include "libANGLE/renderer/vulkan/CLMemoryVk.h" |
| #include "libANGLE/renderer/vulkan/vk_cl_utils.h" |
| #include "libANGLE/renderer/vulkan/vk_renderer.h" |
| #include "libANGLE/renderer/vulkan/vk_utils.h" |
| #include "libANGLE/renderer/vulkan/vk_wrapper.h" |
| |
| #include "libANGLE/renderer/CLMemoryImpl.h" |
| #include "libANGLE/renderer/Format.h" |
| #include "libANGLE/renderer/FormatID_autogen.h" |
| |
| #include "libANGLE/CLBuffer.h" |
| #include "libANGLE/CLContext.h" |
| #include "libANGLE/CLImage.h" |
| #include "libANGLE/CLMemory.h" |
| #include "libANGLE/Error.h" |
| #include "libANGLE/cl_types.h" |
| #include "libANGLE/cl_utils.h" |
| |
| #include "CL/cl_half.h" |
| |
| namespace rx |
| { |
| namespace |
| { |
| bool GetExternalMemoryHandleInfo(const cl_mem_properties *properties, |
| VkExternalMemoryHandleTypeFlagBits *vkExtMemoryHandleType, |
| int32_t *fd) |
| { |
| bool propertyStatus = true; |
| const cl::NameValueProperty *propertyIterator = |
| reinterpret_cast<const cl::NameValueProperty *>(properties); |
| while (propertyIterator->name != 0) |
| { |
| switch (propertyIterator->name) |
| { |
| case CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_FD_KHR: |
| *vkExtMemoryHandleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; |
| break; |
| case CL_EXTERNAL_MEMORY_HANDLE_DMA_BUF_KHR: |
| *vkExtMemoryHandleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; |
| break; |
| case CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_KHR: |
| *vkExtMemoryHandleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; |
| break; |
| case CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_KMT_KHR: |
| *vkExtMemoryHandleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT; |
| break; |
| default: |
| propertyStatus = false; |
| break; |
| } |
| |
| if (propertyStatus) |
| { |
| *fd = *(reinterpret_cast<int32_t *>(propertyIterator->value)); |
| if (*fd < 0) |
| { |
| propertyStatus = false; |
| } |
| break; |
| } |
| propertyIterator++; |
| } |
| |
| return propertyStatus; |
| } |
| |
| } // namespace |
| |
| CLMemoryVk::CLMemoryVk(const cl::Memory &memory) |
| : CLMemoryImpl(memory), |
| mContext(&memory.getContext().getImpl<CLContextVk>()), |
| mRenderer(mContext->getRenderer()), |
| mMappedMemory(nullptr), |
| mMapCount(0), |
| mParent(nullptr) |
| {} |
| |
| CLMemoryVk::~CLMemoryVk() |
| { |
| mContext->mAssociatedObjects->mMemories.erase(mMemory.getNative()); |
| } |
| |
| VkBufferUsageFlags CLMemoryVk::getVkUsageFlags() |
| { |
| return cl_vk::GetBufferUsageFlags(mMemory.getFlags(), |
| mContext->getFeatures().supportsBufferDeviceAddress.enabled); |
| } |
| |
| VkMemoryPropertyFlags CLMemoryVk::getVkMemPropertyFlags() |
| { |
| return cl_vk::GetMemoryPropertyFlags(mMemory.getFlags()); |
| } |
| |
| angle::Result CLMemoryVk::map(uint8_t *&ptrOut, size_t offset) |
| { |
| if (getFlags().intersects(CL_MEM_USE_HOST_PTR)) |
| { |
| // as per spec, the returned pointer for USE_HOST_PTR will be derived from the hostptr... |
| ASSERT(mMemory.getHostPtr()); |
| ptrOut = static_cast<uint8_t *>(mMemory.getHostPtr()) + offset; |
| } |
| else |
| { |
| // ...otherwise we just map the VK memory to cpu va space |
| ANGLE_TRY(mapBufferHelper(ptrOut)); |
| ptrOut += offset; |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result CLMemoryVk::copyTo(void *dst, size_t srcOffset, size_t size) |
| { |
| uint8_t *src = nullptr; |
| ANGLE_TRY(mapBufferHelper(src)); |
| src += srcOffset; |
| std::memcpy(dst, src, size); |
| unmapBufferHelper(); |
| return angle::Result::Continue; |
| } |
| |
| angle::Result CLMemoryVk::copyFrom(const void *src, size_t srcOffset, size_t size) |
| { |
| uint8_t *dst = nullptr; |
| ANGLE_TRY(mapBufferHelper(dst)); |
| dst += srcOffset; |
| std::memcpy(dst, src, size); |
| unmapBufferHelper(); |
| return angle::Result::Continue; |
| } |
| |
| // Create a sub-buffer from the given buffer object |
| angle::Result CLMemoryVk::createSubBuffer(const cl::Buffer &buffer, |
| cl::MemFlags flags, |
| size_t size, |
| CLMemoryImpl::Ptr *subBufferOut) |
| { |
| ASSERT(buffer.isSubBuffer()); |
| |
| CLBufferVk *bufferVk = new CLBufferVk(buffer); |
| if (!bufferVk) |
| { |
| ANGLE_CL_RETURN_ERROR(CL_OUT_OF_HOST_MEMORY); |
| } |
| ANGLE_TRY(bufferVk->create(nullptr)); |
| *subBufferOut = CLMemoryImpl::Ptr(bufferVk); |
| |
| return angle::Result::Continue; |
| } |
| |
| CLBufferVk::CLBufferVk(const cl::Buffer &buffer) : CLMemoryVk(buffer) |
| { |
| if (buffer.isSubBuffer()) |
| { |
| mParent = &buffer.getParent()->getImpl<CLBufferVk>(); |
| } |
| mDefaultBufferCreateInfo = {}; |
| mDefaultBufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
| mDefaultBufferCreateInfo.size = buffer.getSize(); |
| mDefaultBufferCreateInfo.usage = getVkUsageFlags(); |
| mDefaultBufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; |
| } |
| |
| CLBufferVk::~CLBufferVk() |
| { |
| if (isMapped()) |
| { |
| unmapBufferHelper(); |
| } |
| mBuffer.destroy(mRenderer); |
| } |
| |
| bool CLBufferVk::isHostPtrAligned() const |
| { |
| VkDeviceSize alignment = |
| mRenderer->getPhysicalDeviceExternalMemoryHostProperties().minImportedHostPointerAlignment; |
| return reinterpret_cast<uintptr_t>(mMemory.getHostPtr()) % alignment == 0 && |
| getSize() % alignment == 0; |
| } |
| |
| bool CLBufferVk::supportsZeroCopy() const |
| { |
| return mRenderer->getFeatures().supportsExternalMemoryHost.enabled && |
| mMemory.getFlags().intersects(CL_MEM_USE_HOST_PTR) && isHostPtrAligned(); |
| } |
| |
| vk::BufferHelper &CLBufferVk::getBuffer() |
| { |
| if (isSubBuffer()) |
| { |
| return getParent()->getBuffer(); |
| } |
| return mBuffer; |
| } |
| |
| // For UHP buffers, the buffer contents and hostptr have to be in sync at appropriate times. Ensure |
| // that if zero copy is not supported. |
| angle::Result CLBufferVk::syncHost(CLBufferVk::SyncHostDirection direction) |
| { |
| switch (direction) |
| { |
| case CLBufferVk::SyncHostDirection::FromHost: |
| if (getFlags().intersects(CL_MEM_USE_HOST_PTR) && !supportsZeroCopy()) |
| { |
| ANGLE_CL_IMPL_TRY_ERROR( |
| setDataImpl(static_cast<const uint8_t *>(getHostPtr()), getSize(), 0), |
| CL_OUT_OF_RESOURCES); |
| } |
| break; |
| case CLBufferVk::SyncHostDirection::ToHost: |
| if (getFlags().intersects(CL_MEM_USE_HOST_PTR) && !supportsZeroCopy()) |
| { |
| ANGLE_TRY(copyTo(getHostPtr(), 0, getSize())); |
| } |
| break; |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| return angle::Result::Continue; |
| } |
| |
| // This is to sync only a rectangular region between hostptr and buffer contents. Intended to be |
| // used for READ/WRITE_RECT. |
| angle::Result CLBufferVk::syncHost(CLBufferVk::SyncHostDirection direction, cl::BufferRect hostRect) |
| { |
| switch (direction) |
| { |
| case CLBufferVk::SyncHostDirection::FromHost: |
| if (getFlags().intersects(CL_MEM_USE_HOST_PTR) && !supportsZeroCopy()) |
| { |
| ANGLE_TRY(setRect(getHostPtr(), hostRect, hostRect)); |
| } |
| break; |
| case CLBufferVk::SyncHostDirection::ToHost: |
| if (getFlags().intersects(CL_MEM_USE_HOST_PTR) && !supportsZeroCopy()) |
| { |
| ANGLE_TRY(getRect(hostRect, hostRect, getHostPtr())); |
| } |
| break; |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| return angle::Result::Continue; |
| } |
| |
| angle::Result CLBufferVk::create(void *hostPtr) |
| { |
| const cl_mem_properties *properties = getFrontendObject().getProperties().data(); |
| if (properties) |
| { |
| const cl::NameValueProperty *property = |
| reinterpret_cast<const cl::NameValueProperty *>(properties); |
| if (property->name != 0) |
| { |
| return createWithProperties(); |
| } |
| } |
| |
| if (!isSubBuffer()) |
| { |
| VkBufferCreateInfo createInfo = mDefaultBufferCreateInfo; |
| createInfo.size = getSize(); |
| VkMemoryPropertyFlags memFlags = getVkMemPropertyFlags(); |
| |
| if (supportsZeroCopy()) |
| { |
| return mBuffer.initHostExternal(mContext, memFlags, createInfo, hostPtr); |
| } |
| |
| ANGLE_CL_IMPL_TRY_ERROR(mBuffer.init(mContext, createInfo, memFlags), CL_OUT_OF_RESOURCES); |
| // We need to copy the data from hostptr in the case of CHP buffer. |
| if (getFlags().intersects(CL_MEM_COPY_HOST_PTR)) |
| { |
| ANGLE_CL_IMPL_TRY_ERROR(setDataImpl(static_cast<uint8_t *>(hostPtr), getSize(), 0), |
| CL_OUT_OF_RESOURCES); |
| } |
| ANGLE_TRY(syncHost(CLBufferVk::SyncHostDirection::FromHost)); |
| } |
| return angle::Result::Continue; |
| } |
| |
| angle::Result CLBufferVk::createWithProperties() |
| { |
| ASSERT(!isSubBuffer()); |
| |
| int32_t sharedBufferFD = -1; |
| VkExternalMemoryHandleTypeFlagBits vkExtMemoryHandleType; |
| const cl_mem_properties *properties = getFrontendObject().getProperties().data(); |
| if (GetExternalMemoryHandleInfo(properties, &vkExtMemoryHandleType, &sharedBufferFD)) |
| { |
| #if defined(ANGLE_PLATFORM_WINDOWS) |
| UNIMPLEMENTED(); |
| ANGLE_CL_RETURN_ERROR(CL_OUT_OF_RESOURCES); |
| #else |
| VkBufferCreateInfo createInfo = mDefaultBufferCreateInfo; |
| createInfo.size = getSize(); |
| VkMemoryPropertyFlags memFlags = getVkMemPropertyFlags(); |
| |
| // VK_KHR_external_memory assumes the ownership of the buffer as part of the import |
| // operation. No such requirement is present with cl_khr_external_memory and |
| // cl_arm_import_memory extension. So we dup the fd for now, and let the application still |
| // hold on to the fd. |
| if (IsError(mBuffer.initAndAcquireFromExternalMemory( |
| mContext, memFlags, createInfo, vkExtMemoryHandleType, dup(sharedBufferFD)))) |
| { |
| ANGLE_CL_RETURN_ERROR(CL_OUT_OF_RESOURCES); |
| } |
| #endif |
| } |
| else |
| { |
| // Don't expect to be here, as validation layer should have caught unsupported uses. |
| UNREACHABLE(); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result CLBufferVk::copyToWithPitch(void *hostPtr, |
| size_t srcOffset, |
| size_t size, |
| size_t rowPitch, |
| size_t slicePitch, |
| cl::Extents region, |
| const size_t elementSize) |
| { |
| uint8_t *ptrInBase = nullptr; |
| uint8_t *ptrOutBase = nullptr; |
| cl::BufferRect stagingBufferRect{ |
| {static_cast<int>(0), static_cast<int>(0), static_cast<int>(0)}, |
| {region.width, region.height, region.depth}, |
| 0, |
| 0, |
| elementSize}; |
| |
| ptrOutBase = static_cast<uint8_t *>(hostPtr); |
| ANGLE_TRY(mapBufferHelper(ptrInBase)); |
| cl::Defer deferUnmap([this]() { unmapBufferHelper(); }); |
| |
| for (size_t slice = 0; slice < region.depth; slice++) |
| { |
| for (size_t row = 0; row < region.height; row++) |
| { |
| size_t stagingBufferOffset = stagingBufferRect.getRowOffset(slice, row); |
| size_t hostPtrOffset = (slice * slicePitch + row * rowPitch); |
| uint8_t *dst = ptrOutBase + hostPtrOffset; |
| uint8_t *src = ptrInBase + stagingBufferOffset; |
| memcpy(dst, src, region.width * elementSize); |
| } |
| } |
| return angle::Result::Continue; |
| } |
| |
| angle::Result CLBufferVk::mapBufferHelper(uint8_t *&ptrOut) |
| { |
| if (!isMapped()) |
| { |
| if (isSubBuffer()) |
| { |
| return mapParentBufferHelper(ptrOut); |
| } |
| |
| std::lock_guard<angle::SimpleMutex> lock(mMutex); |
| ANGLE_TRY(mBuffer.map(mContext, &mMappedMemory)); |
| ++mMapCount; |
| } |
| ptrOut = mMappedMemory; |
| ASSERT(ptrOut); |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result CLBufferVk::mapParentBufferHelper(uint8_t *&ptrOut) |
| { |
| ASSERT(getParent()); |
| |
| ANGLE_TRY(getParent()->mapBufferHelper(ptrOut)); |
| ptrOut += getOffset(); |
| return angle::Result::Continue; |
| } |
| |
| void CLBufferVk::unmapBufferHelper() |
| { |
| if (isSubBuffer()) |
| { |
| getParent()->unmapBufferHelper(); |
| return; |
| } |
| |
| std::lock_guard<angle::SimpleMutex> lock(mMutex); |
| getBuffer().unmap(mContext->getRenderer()); |
| if (--mMapCount == 0u) |
| { |
| mMappedMemory = nullptr; |
| } |
| } |
| |
| angle::Result CLBufferVk::updateRect(UpdateRectOperation op, |
| void *data, |
| const cl::BufferRect &dataRect, |
| const cl::BufferRect &bufferRect) |
| { |
| ASSERT(dataRect.valid() && bufferRect.valid()); |
| ASSERT(dataRect.mSize == bufferRect.mSize && dataRect.mElementSize == bufferRect.mElementSize); |
| |
| uint8_t *bufferPtr = nullptr; |
| ANGLE_TRY(mapBufferHelper(bufferPtr)); |
| cl::Defer deferUnmap([this]() { unmapBufferHelper(); }); |
| |
| uint8_t *dataUint8Ptr = reinterpret_cast<uint8_t *>(data); |
| for (size_t slice = 0; slice < bufferRect.mSize.depth; slice++) |
| { |
| for (size_t row = 0; row < bufferRect.mSize.height; row++) |
| { |
| uint8_t *offsetDataPtr = dataUint8Ptr + dataRect.getRowOffset(slice, row); |
| uint8_t *offsetBufferPtr = bufferPtr + bufferRect.getRowOffset(slice, row); |
| size_t updateSize = dataRect.mSize.width * dataRect.mElementSize; |
| |
| switch (op) |
| { |
| case UpdateRectOperation::Read: |
| { |
| // Read from this buffer |
| memcpy(offsetDataPtr, offsetBufferPtr, updateSize); |
| break; |
| } |
| case UpdateRectOperation::Write: |
| { |
| // Write to this buffer |
| memcpy(offsetBufferPtr, offsetDataPtr, updateSize); |
| break; |
| } |
| default: |
| { |
| UNREACHABLE(); |
| break; |
| } |
| } |
| } |
| } |
| return angle::Result::Continue; |
| } |
| |
| angle::Result CLBufferVk::setRect(const void *data, |
| const cl::BufferRect &dataRect, |
| const cl::BufferRect &bufferRect) |
| { |
| return updateRect(UpdateRectOperation::Write, const_cast<void *>(data), dataRect, bufferRect); |
| } |
| |
| angle::Result CLBufferVk::getRect(const cl::BufferRect &bufferRect, |
| const cl::BufferRect &dataRect, |
| void *outData) |
| { |
| return updateRect(UpdateRectOperation::Read, outData, dataRect, bufferRect); |
| } |
| |
| // offset is for mapped pointer |
| angle::Result CLBufferVk::setDataImpl(const uint8_t *data, size_t size, size_t offset) |
| { |
| // buffer cannot be in use state |
| ASSERT(mBuffer.valid()); |
| ASSERT(!isCurrentlyInUse()); |
| ASSERT(size + offset <= getSize()); |
| ASSERT(data != nullptr); |
| |
| // Assuming host visible buffers for now |
| // TODO: http://anglebug.com/42267019 |
| if (!mBuffer.isHostVisible()) |
| { |
| UNIMPLEMENTED(); |
| ANGLE_CL_RETURN_ERROR(CL_OUT_OF_RESOURCES); |
| } |
| |
| uint8_t *mapPointer = nullptr; |
| ANGLE_TRY(mBuffer.mapWithOffset(mContext, &mapPointer, offset)); |
| ASSERT(mapPointer != nullptr); |
| |
| std::memcpy(mapPointer, data, size); |
| mBuffer.unmap(mRenderer); |
| |
| return angle::Result::Continue; |
| } |
| |
| bool CLBufferVk::isCurrentlyInUse() const |
| { |
| return !mRenderer->hasResourceUseFinished(mBuffer.getResourceUse()); |
| } |
| |
| template <> |
| CLBufferVk *CLImageVk::getParent<CLBufferVk>() const |
| { |
| if (mParent) |
| { |
| ASSERT(cl::IsBufferType(getParentType())); |
| return static_cast<CLBufferVk *>(mParent); |
| } |
| return nullptr; |
| } |
| |
| template <> |
| CLImageVk *CLImageVk::getParent<CLImageVk>() const |
| { |
| if (mParent) |
| { |
| ASSERT(cl::IsImageType(getParentType())); |
| return static_cast<CLImageVk *>(mParent); |
| } |
| return nullptr; |
| } |
| |
| VkImageUsageFlags CLImageVk::getVkImageUsageFlags() |
| { |
| VkImageUsageFlags usageFlags = vk::kImageUsageTransferBits; |
| |
| if (mMemory.getFlags().intersects(CL_MEM_WRITE_ONLY)) |
| { |
| usageFlags |= VK_IMAGE_USAGE_STORAGE_BIT; |
| } |
| else if (mMemory.getFlags().intersects(CL_MEM_READ_ONLY)) |
| { |
| usageFlags |= VK_IMAGE_USAGE_SAMPLED_BIT; |
| } |
| else |
| { |
| usageFlags |= VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT; |
| } |
| |
| return usageFlags; |
| } |
| |
| VkImageType CLImageVk::getVkImageType(const cl::ImageDescriptor &desc) |
| { |
| VkImageType imageType = VK_IMAGE_TYPE_MAX_ENUM; |
| |
| switch (desc.type) |
| { |
| case cl::MemObjectType::Image1D_Buffer: |
| case cl::MemObjectType::Image1D: |
| case cl::MemObjectType::Image1D_Array: |
| return VK_IMAGE_TYPE_1D; |
| case cl::MemObjectType::Image2D: |
| case cl::MemObjectType::Image2D_Array: |
| return VK_IMAGE_TYPE_2D; |
| case cl::MemObjectType::Image3D: |
| return VK_IMAGE_TYPE_3D; |
| default: |
| UNREACHABLE(); |
| } |
| |
| return imageType; |
| } |
| |
| angle::Result CLImageVk::getOrCreateStagingBuffer(CLBufferVk **clBufferOut) |
| { |
| ASSERT(clBufferOut && "cannot pass nullptr to clBufferOut!"); |
| |
| std::lock_guard<angle::SimpleMutex> lock(mMutex); |
| |
| if (!cl::Buffer::IsValid(mStagingBuffer)) |
| { |
| mStagingBuffer = cl::Buffer::Cast(mContext->getFrontendObject().createBuffer( |
| nullptr, cl::MemFlags(CL_MEM_READ_WRITE), getSize(), nullptr)); |
| if (!cl::Buffer::IsValid(mStagingBuffer)) |
| { |
| ANGLE_CL_RETURN_ERROR(CL_OUT_OF_RESOURCES); |
| } |
| } |
| *clBufferOut = &mStagingBuffer->getImpl<CLBufferVk>(); |
| return angle::Result::Continue; |
| } |
| |
| angle::Result CLImageVk::copyStagingFrom(void *ptr, size_t offset, size_t size) |
| { |
| uint8_t *ptrOut; |
| uint8_t *ptrIn = static_cast<uint8_t *>(ptr); |
| |
| ANGLE_TRY(mapBufferHelper(ptrOut)); |
| cl::Defer deferUnmap([this]() { unmapBufferHelper(); }); |
| |
| std::memcpy(ptrOut, ptrIn + offset, size); |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result CLImageVk::copyStagingTo(void *ptr, size_t offset, size_t size) |
| { |
| uint8_t *ptrOut; |
| |
| ANGLE_TRY(mapBufferHelper(ptrOut)); |
| cl::Defer deferUnmap([this]() { unmapBufferHelper(); }); |
| |
| std::memcpy(ptr, ptrOut + offset, size); |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result CLImageVk::copyStagingToFromWithPitch(void *hostPtr, |
| const cl::Extents ®ion, |
| const size_t rowPitch, |
| const size_t slicePitch, |
| StagingBufferCopyDirection copyStagingTo) |
| { |
| uint8_t *ptrInBase = nullptr; |
| uint8_t *ptrOutBase = nullptr; |
| cl::BufferRect stagingBufferRect{ |
| {}, {region.width, region.height, region.depth}, 0, 0, getElementSize()}; |
| |
| if (copyStagingTo == StagingBufferCopyDirection::ToHost) |
| { |
| ptrOutBase = static_cast<uint8_t *>(hostPtr); |
| ANGLE_TRY(mapBufferHelper(ptrInBase)); |
| } |
| else |
| { |
| ptrInBase = static_cast<uint8_t *>(hostPtr); |
| ANGLE_TRY(mapBufferHelper(ptrOutBase)); |
| } |
| cl::Defer deferUnmap([this]() { unmapBufferHelper(); }); |
| |
| for (size_t slice = 0; slice < region.depth; slice++) |
| { |
| for (size_t row = 0; row < region.height; row++) |
| { |
| size_t stagingBufferOffset = stagingBufferRect.getRowOffset(slice, row); |
| size_t hostPtrOffset = (slice * slicePitch + row * rowPitch); |
| uint8_t *dst = (copyStagingTo == StagingBufferCopyDirection::ToHost) |
| ? ptrOutBase + hostPtrOffset |
| : ptrOutBase + stagingBufferOffset; |
| uint8_t *src = (copyStagingTo == StagingBufferCopyDirection::ToHost) |
| ? ptrInBase + stagingBufferOffset |
| : ptrInBase + hostPtrOffset; |
| memcpy(dst, src, region.width * getElementSize()); |
| } |
| } |
| return angle::Result::Continue; |
| } |
| |
| CLImageVk::CLImageVk(const cl::Image &image) |
| : CLMemoryVk(image), |
| mExtent(cl::GetExtentFromDescriptor(image.getDescriptor())), |
| mAngleFormat(cl::GetImageAngleFormat(image.getFormat())), |
| mStagingBuffer(nullptr), |
| mImageViewType(cl_vk::GetImageViewType(image.getDescriptor().type)), |
| mIsImage2DFromBuffer(false) |
| { |
| if (image.getParent()) |
| { |
| mParent = &image.getParent()->getImpl<CLMemoryVk>(); |
| mIsImage2DFromBuffer = |
| cl::Is2DImage(image.getType()) && cl::IsBufferType(mParent->getType()); |
| } |
| } |
| |
| CLImageVk::~CLImageVk() |
| { |
| if (isMapped()) |
| { |
| unmap(); |
| } |
| |
| if (mBufferViews.isInitialized()) |
| { |
| mBufferViews.release(mContext->getRenderer()); |
| } |
| |
| mImage.destroy(mRenderer); |
| mImageView.destroy(mContext->getDevice()); |
| if (cl::Memory::IsValid(mStagingBuffer) && mStagingBuffer->release()) |
| { |
| SafeDelete(mStagingBuffer); |
| } |
| } |
| |
| angle::Result CLImageVk::createFromBuffer() |
| { |
| ASSERT(mParent); |
| ASSERT(IsBufferType(getParentType())); |
| |
| // initialize the buffer views |
| mBufferViews.init(mContext->getRenderer(), 0, getSize()); |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result CLImageVk::create(void *hostPtr) |
| { |
| // There will be a parent memory object in the following cases |
| // - image1d_buffer |
| // - image2d_from_buffer |
| // - image2d from image2d |
| if (mParent) |
| { |
| if (getType() == cl::MemObjectType::Image1D_Buffer) |
| { |
| return createFromBuffer(); |
| } |
| else if (mIsImage2DFromBuffer) |
| { |
| // Request for image2d_from_buffer - nothing to do for now |
| // We would liked to create this as a buffer view as well, clspv for now cannot mark |
| // this usage with texel buffer descriptor type usage. |
| } |
| else |
| { |
| // image2d from image2d is unimplemented at the moment |
| UNIMPLEMENTED(); |
| ANGLE_CL_RETURN_ERROR(CL_OUT_OF_RESOURCES); |
| } |
| } |
| |
| ANGLE_CL_IMPL_TRY_ERROR(mImage.initStaging(mContext, false, getVkImageType(getDescriptor()), |
| cl_vk::GetExtent(mExtent), mAngleFormat, |
| mAngleFormat, VK_SAMPLE_COUNT_1_BIT, |
| getVkImageUsageFlags(), 1, (uint32_t)getArraySize()), |
| CL_OUT_OF_RESOURCES); |
| |
| if (getFlags().intersects(CL_MEM_USE_HOST_PTR | CL_MEM_COPY_HOST_PTR)) |
| { |
| // in case of image2d from buffer, hostptr flag could be inherited from parent flags |
| if (!mIsImage2DFromBuffer) |
| { |
| ASSERT(hostPtr); |
| |
| if (getDescriptor().rowPitch == 0 && getDescriptor().slicePitch == 0) |
| { |
| ANGLE_CL_IMPL_TRY_ERROR(copyStagingFrom(hostPtr, 0, getSize()), |
| CL_OUT_OF_RESOURCES); |
| } |
| else |
| { |
| ANGLE_TRY(copyStagingToFromWithPitch( |
| hostPtr, {mExtent.width, mExtent.height, mExtent.depth}, |
| getDescriptor().rowPitch, getDescriptor().slicePitch, |
| StagingBufferCopyDirection::ToStagingBuffer)); |
| } |
| } |
| |
| // copy over the hostptr bits/parent buffer here to image in a one-off copy cmd |
| CLBufferVk *stagingBuffer = nullptr; |
| ANGLE_TRY(getOrCreateStagingBuffer(&stagingBuffer)); |
| ASSERT(stagingBuffer); |
| VkBufferImageCopy copyRegion = cl_vk::CalculateBufferImageCopyRegion( |
| mIsImage2DFromBuffer ? getParent<CLBufferVk>()->getOffset() : 0, |
| mIsImage2DFromBuffer ? static_cast<uint32_t>(getRowPitch()) : 0, 0, cl::kOffsetZero, |
| getImageExtent(), this); |
| ANGLE_CL_IMPL_TRY_ERROR( |
| mImage.copyToBufferOneOff(mContext, |
| mIsImage2DFromBuffer |
| ? &static_cast<CLBufferVk *>(mParent)->getBuffer() |
| : &stagingBuffer->getBuffer(), |
| copyRegion), |
| CL_OUT_OF_RESOURCES); |
| } |
| |
| ANGLE_TRY(initImageViewImpl()); |
| return angle::Result::Continue; |
| } |
| |
| angle::Result CLImageVk::initImageViewImpl() |
| { |
| VkImageViewCreateInfo viewInfo = {}; |
| viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; |
| viewInfo.flags = 0; |
| viewInfo.image = getImage().getImage().getHandle(); |
| viewInfo.format = getImage().getActualVkFormat(mContext->getRenderer()); |
| viewInfo.viewType = mImageViewType; |
| |
| viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| // We don't support mip map levels and should have been validated |
| ASSERT(getDescriptor().numMipLevels == 0); |
| viewInfo.subresourceRange.baseMipLevel = getDescriptor().numMipLevels; |
| viewInfo.subresourceRange.levelCount = 1; |
| viewInfo.subresourceRange.baseArrayLayer = 0; |
| viewInfo.subresourceRange.layerCount = static_cast<uint32_t>(getArraySize()); |
| |
| // apply CL order swizzle here OR We can do swizzle in shaders for read/write |
| viewInfo.components = cl_vk::GetChannelComponentMapping(getFormat().image_channel_order); |
| |
| VkImageViewUsageCreateInfo imageViewUsageCreateInfo = {}; |
| imageViewUsageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO; |
| imageViewUsageCreateInfo.usage = getVkImageUsageFlags(); |
| |
| viewInfo.pNext = &imageViewUsageCreateInfo; |
| |
| ANGLE_VK_TRY(mContext, mImageView.init(mContext->getDevice(), viewInfo)); |
| return angle::Result::Continue; |
| } |
| |
| bool CLImageVk::isCurrentlyInUse() const |
| { |
| return !mRenderer->hasResourceUseFinished(mImage.getResourceUse()); |
| } |
| |
| bool CLImageVk::containsHostMemExtension() |
| { |
| const vk::ExtensionNameList &enabledDeviceExtensions = mRenderer->getEnabledDeviceExtensions(); |
| return std::find(enabledDeviceExtensions.begin(), enabledDeviceExtensions.end(), |
| "VK_EXT_external_memory_host") != enabledDeviceExtensions.end(); |
| } |
| |
| angle::Result CLImageVk::fillImageWithColor(const cl::Offset &origin, |
| const cl::Extents ®ion, |
| cl::PixelColor packedColor) |
| { |
| size_t elementSize = getElementSize(); |
| cl::BufferRect stagingBufferRect{ |
| {}, {mExtent.width, mExtent.height, mExtent.depth}, 0, 0, elementSize}; |
| |
| uint8_t *imagePtr = nullptr; |
| ANGLE_TRY(mapBufferHelper(imagePtr)); |
| cl::Defer deferUnmap([this]() { unmapBufferHelper(); }); |
| |
| uint8_t *ptrBase = imagePtr + (origin.z * stagingBufferRect.getSlicePitch()) + |
| (origin.y * stagingBufferRect.getRowPitch()) + (origin.x * elementSize); |
| |
| for (size_t slice = 0; slice < region.depth; slice++) |
| { |
| for (size_t row = 0; row < region.height; row++) |
| { |
| size_t stagingBufferOffset = stagingBufferRect.getRowOffset(slice, row); |
| uint8_t *pixelPtr = ptrBase + stagingBufferOffset; |
| for (size_t x = 0; x < region.width; x++) |
| { |
| memcpy(pixelPtr, &packedColor, elementSize); |
| pixelPtr += elementSize; |
| } |
| } |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| cl::Extents CLImageVk::getExtentForCopy(const cl::Extents ®ion) |
| { |
| cl::Extents extent = {}; |
| extent.width = region.width; |
| extent.height = region.height; |
| extent.depth = region.depth; |
| switch (getDescriptor().type) |
| { |
| case cl::MemObjectType::Image1D_Array: |
| |
| extent.height = 1; |
| extent.depth = 1; |
| break; |
| case cl::MemObjectType::Image2D_Array: |
| extent.depth = 1; |
| break; |
| default: |
| break; |
| } |
| return extent; |
| } |
| |
| cl::Offset CLImageVk::getOffsetForCopy(const cl::Offset &origin) |
| { |
| cl::Offset offset = {}; |
| offset.x = origin.x; |
| offset.y = origin.y; |
| offset.z = origin.z; |
| switch (getDescriptor().type) |
| { |
| case cl::MemObjectType::Image1D_Array: |
| offset.y = 0; |
| offset.z = 0; |
| break; |
| case cl::MemObjectType::Image2D_Array: |
| offset.z = 0; |
| break; |
| default: |
| break; |
| } |
| return offset; |
| } |
| |
| VkImageSubresourceLayers CLImageVk::getSubresourceLayersForCopy(const cl::Offset &origin, |
| const cl::Extents ®ion, |
| cl::MemObjectType copyToType, |
| ImageCopyWith imageCopy) |
| { |
| VkImageSubresourceLayers subresource = {}; |
| subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| subresource.mipLevel = 0; |
| switch (getDescriptor().type) |
| { |
| case cl::MemObjectType::Image1D_Array: |
| subresource.baseArrayLayer = static_cast<uint32_t>(origin.y); |
| if (imageCopy == ImageCopyWith::Image) |
| { |
| subresource.layerCount = static_cast<uint32_t>(region.height); |
| } |
| else |
| { |
| subresource.layerCount = static_cast<uint32_t>(getArraySize()); |
| } |
| break; |
| case cl::MemObjectType::Image2D_Array: |
| subresource.baseArrayLayer = static_cast<uint32_t>(origin.z); |
| if (copyToType == cl::MemObjectType::Image2D || |
| copyToType == cl::MemObjectType::Image3D) |
| { |
| subresource.layerCount = 1; |
| } |
| else if (imageCopy == ImageCopyWith::Image) |
| { |
| subresource.layerCount = static_cast<uint32_t>(region.depth); |
| } |
| else |
| { |
| subresource.layerCount = static_cast<uint32_t>(getArraySize()); |
| } |
| break; |
| default: |
| subresource.baseArrayLayer = 0; |
| subresource.layerCount = 1; |
| break; |
| } |
| return subresource; |
| } |
| |
| angle::Result CLImageVk::mapBufferHelper(uint8_t *&ptrOut) |
| { |
| if (!isMapped()) |
| { |
| if (mParent) |
| { |
| return mapParentBufferHelper(ptrOut); |
| } |
| |
| CLBufferVk *stagingBuffer = nullptr; |
| ANGLE_TRY(getOrCreateStagingBuffer(&stagingBuffer)); |
| ASSERT(stagingBuffer); |
| ANGLE_TRY(stagingBuffer->mapBufferHelper(mMappedMemory)); |
| } |
| ASSERT(mMappedMemory); |
| ptrOut = mMappedMemory; |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result CLImageVk::mapParentBufferHelper(uint8_t *&ptrOut) |
| { |
| ASSERT(mParent); |
| |
| if (cl::IsBufferType(getParentType())) |
| { |
| ANGLE_TRY(getParent<CLBufferVk>()->mapBufferHelper(ptrOut)); |
| } |
| else if (cl::IsImageType(getParentType())) |
| { |
| ANGLE_TRY(getParent<CLImageVk>()->mapBufferHelper(ptrOut)); |
| } |
| else |
| { |
| UNREACHABLE(); |
| } |
| ptrOut += getOffset(); |
| return angle::Result::Continue; |
| } |
| |
| void CLImageVk::unmapBufferHelper() |
| { |
| if (mParent) |
| { |
| getParent<CLImageVk>()->unmapBufferHelper(); |
| return; |
| } |
| ASSERT(mStagingBuffer); |
| mStagingBuffer->getImpl<CLBufferVk>().unmapBufferHelper(); |
| } |
| |
| size_t CLImageVk::getRowPitch() const |
| { |
| return getFrontendObject().getRowSize(); |
| } |
| |
| size_t CLImageVk::getSlicePitch() const |
| { |
| return getFrontendObject().getSliceSize(); |
| } |
| |
| cl::MemObjectType CLImageVk::getParentType() const |
| { |
| if (mParent) |
| { |
| return mParent->getType(); |
| } |
| return cl::MemObjectType::InvalidEnum; |
| } |
| |
| angle::Result CLImageVk::getBufferView(const vk::BufferView **viewOut) |
| { |
| if (!mBufferViews.isInitialized()) |
| { |
| ANGLE_CL_RETURN_ERROR(CL_OUT_OF_RESOURCES); |
| } |
| |
| CLBufferVk *parent = getParent<CLBufferVk>(); |
| |
| return mBufferViews.getView( |
| mContext, parent->getBuffer(), parent->getOffset(), |
| mContext->getRenderer()->getFormat(cl::GetImageAngleFormat(getFormat())), viewOut, nullptr); |
| } |
| |
| } // namespace rx |