diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index b8e484321..2fb7c33a0 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -244,6 +244,7 @@ + @@ -316,6 +317,7 @@ + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index 351cfd760..1dcbacabe 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -70,6 +70,9 @@ Vulkan + + Vulkan + @@ -127,6 +130,9 @@ Vulkan + + Vulkan + diff --git a/Common/Vulkan/VulkanContext.h b/Common/Vulkan/VulkanContext.h index e6eda6376..8671f2c24 100644 --- a/Common/Vulkan/VulkanContext.h +++ b/Common/Vulkan/VulkanContext.h @@ -1,28 +1,3 @@ -/* - * Vulkan Samples Kit - * - * Copyright (C) 2015 Valve Corporation - * Copyright (C) 2015 Google, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - #ifndef UTIL_INIT #define UTIL_INIT @@ -52,7 +27,7 @@ #include "Common/Vulkan/VulkanLoader.h" - /* Amount of time, in nanoseconds, to wait for a command buffer to complete */ +// Amount of time, in nanoseconds, to wait for a command buffer to complete #define FENCE_TIMEOUT 10000000000 #if defined(NDEBUG) && defined(__GNUC__) @@ -88,6 +63,15 @@ public: void QueueDeletePipelineCache(VkPipelineCache pipelineCache) { pipelineCaches_.push_back(pipelineCache); } void Take(VulkanDeleteList &del) { + assert(descPools_.size() == 0); + assert(modules_.size() == 0); + assert(buffers_.size() == 0); + assert(bufferViews_.size() == 0); + assert(images_.size() == 0); + assert(imageViews_.size() == 0); + assert(deviceMemory_.size() == 0); + assert(samplers_.size() == 0); + assert(pipelineCaches_.size() == 0); descPools_ = std::move(del.descPools_); modules_ = std::move(del.modules_); buffers_ = std::move(del.buffers_); @@ -340,106 +324,6 @@ private: std::vector cmdQueue_; }; -// Use these to push vertex, index and uniform data. Generally you'll have two of these -// and alternate on each frame. -// TODO: Make it possible to suballocate pushbuffers from a large DeviceMemory block. -// We'll have two of these that we alternate between on each frame. -// TODO: Make this auto-grow and shrink. Need to be careful about returning and using the new -// buffer handle on overflow. -class VulkanPushBuffer { -public: - VulkanPushBuffer(VulkanContext *vulkan, size_t size) : offset_(0), size_(size), writePtr_(nullptr), deviceMemory_(0) { - VkDevice device = vulkan->GetDevice(); - - VkBufferCreateInfo b = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; - b.size = size; - b.flags = 0; - b.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - b.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - b.queueFamilyIndexCount = 0; - b.pQueueFamilyIndices = nullptr; - VkResult res = vkCreateBuffer(device, &b, nullptr, &buffer_); - assert(VK_SUCCESS == res); - - // Okay, that's the buffer. Now let's allocate some memory for it. - VkMemoryAllocateInfo alloc = {}; - alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - alloc.pNext = nullptr; - vulkan->MemoryTypeFromProperties(0xFFFFFFFF, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &alloc.memoryTypeIndex); - alloc.allocationSize = size; - - res = vkAllocateMemory(device, &alloc, nullptr, &deviceMemory_); - assert(VK_SUCCESS == res); - res = vkBindBufferMemory(device, buffer_, deviceMemory_, 0); - assert(VK_SUCCESS == res); - } - - void Destroy(VulkanContext *vulkan) { - vulkan->Delete().QueueDeleteBuffer(buffer_); - vulkan->Delete().QueueDeleteDeviceMemory(deviceMemory_); - } - - void Reset() { offset_ = 0; } - - void Begin(VkDevice device) { - offset_ = 0; - VkResult res = vkMapMemory(device, deviceMemory_, 0, size_, 0, (void **)(&writePtr_)); - assert(VK_SUCCESS == res); - } - - void End(VkDevice device) { - vkUnmapMemory(device, deviceMemory_); - writePtr_ = nullptr; - } - - size_t Allocate(size_t numBytes) { - size_t out = offset_; - offset_ += (numBytes + 3) & ~3; // Round up to 4 bytes. - if (offset_ >= size_) { - // TODO: Allocate a second buffer, then combine them on the next frame. -#ifdef _WIN32 - DebugBreak(); -#endif - } - return out; - } - - // TODO: Add alignment support? - // Returns the offset that should be used when binding this buffer to get this data. - size_t Push(const void *data, size_t size) { - size_t off = Allocate(size); - memcpy(writePtr_ + off, data, size); - return off; - } - - uint32_t PushAligned(const void *data, size_t size, int align) { - offset_ = (offset_ + align - 1) & ~(align - 1); - size_t off = Allocate(size); - memcpy(writePtr_ + off, data, size); - return (uint32_t)off; - } - - size_t GetOffset() const { - return offset_; - } - - // "Zero-copy" variant - you can write the data directly as you compute it. - void *Push(size_t size, size_t *bindOffset) { - size_t off = Allocate(size); - *bindOffset = off; - return writePtr_ + off; - } - - VkBuffer GetVkBuffer() const { return buffer_; } - -private: - VkDeviceMemory deviceMemory_; - VkBuffer buffer_; - size_t offset_; - size_t size_; - uint8_t *writePtr_; -}; - // Stand-alone utility functions void VulkanBeginCommandBuffer(VkCommandBuffer cmd); void TransitionImageLayout( diff --git a/Common/Vulkan/VulkanMemory.cpp b/Common/Vulkan/VulkanMemory.cpp new file mode 100644 index 000000000..2ffcc15ed --- /dev/null +++ b/Common/Vulkan/VulkanMemory.cpp @@ -0,0 +1,47 @@ +// Copyright (c) 2016- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +// Additionally, Common/Vulkan/* , including this file, are also licensed +// under the public domain. + +#include "Common/Vulkan/VulkanMemory.h" + +VulkanPushBuffer::VulkanPushBuffer(VulkanContext *vulkan, size_t size) : offset_(0), size_(size), writePtr_(nullptr), deviceMemory_(0) { + VkDevice device = vulkan->GetDevice(); + + VkBufferCreateInfo b = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + b.size = size; + b.flags = 0; + b.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + b.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + b.queueFamilyIndexCount = 0; + b.pQueueFamilyIndices = nullptr; + VkResult res = vkCreateBuffer(device, &b, nullptr, &buffer_); + assert(VK_SUCCESS == res); + + // Okay, that's the buffer. Now let's allocate some memory for it. + VkMemoryAllocateInfo alloc = {}; + alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc.pNext = nullptr; + vulkan->MemoryTypeFromProperties(0xFFFFFFFF, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &alloc.memoryTypeIndex); + alloc.allocationSize = size; + + res = vkAllocateMemory(device, &alloc, nullptr, &deviceMemory_); + assert(VK_SUCCESS == res); + res = vkBindBufferMemory(device, buffer_, deviceMemory_, 0); + assert(VK_SUCCESS == res); +} diff --git a/Common/Vulkan/VulkanMemory.h b/Common/Vulkan/VulkanMemory.h new file mode 100644 index 000000000..eb0ab011c --- /dev/null +++ b/Common/Vulkan/VulkanMemory.h @@ -0,0 +1,93 @@ +#pragma once + +#include "Common/Vulkan/VulkanContext.h" + +// VulkanMemory +// +// Vulkan memory management utils. + +// VulkanPushBuffer +// Simple incrementing allocator. +// Use these to push vertex, index and uniform data. Generally you'll have two of these +// and alternate on each frame. Make sure not to reset until the fence from the last time you used it +// has completed. +// +// TODO: Make it possible to suballocate pushbuffers from a large DeviceMemory block. +// TODO: Make this auto-grow and shrink. Need to be careful about returning and using the new +// buffer handle on overflow. +class VulkanPushBuffer { +public: + VulkanPushBuffer(VulkanContext *vulkan, size_t size); + + ~VulkanPushBuffer() { + assert(buffer_ == VK_NULL_HANDLE); + assert(deviceMemory_ == VK_NULL_HANDLE); + } + + void Destroy(VulkanContext *vulkan) { + vulkan->Delete().QueueDeleteBuffer(buffer_); + vulkan->Delete().QueueDeleteDeviceMemory(deviceMemory_); + buffer_ = VK_NULL_HANDLE; + deviceMemory_ = VK_NULL_HANDLE; + } + + void Reset() { offset_ = 0; } + + void Begin(VkDevice device) { + offset_ = 0; + VkResult res = vkMapMemory(device, deviceMemory_, 0, size_, 0, (void **)(&writePtr_)); + assert(VK_SUCCESS == res); + } + + void End(VkDevice device) { + vkUnmapMemory(device, deviceMemory_); + writePtr_ = nullptr; + } + + size_t Allocate(size_t numBytes) { + size_t out = offset_; + offset_ += (numBytes + 3) & ~3; // Round up to 4 bytes. + if (offset_ >= size_) { + // TODO: Allocate a second buffer, then combine them on the next frame. +#ifdef _WIN32 + DebugBreak(); +#endif + } + return out; + } + + // TODO: Add alignment support? + // Returns the offset that should be used when binding this buffer to get this data. + size_t Push(const void *data, size_t size) { + size_t off = Allocate(size); + memcpy(writePtr_ + off, data, size); + return off; + } + + uint32_t PushAligned(const void *data, size_t size, int align) { + offset_ = (offset_ + align - 1) & ~(align - 1); + size_t off = Allocate(size); + memcpy(writePtr_ + off, data, size); + return (uint32_t)off; + } + + size_t GetOffset() const { + return offset_; + } + + // "Zero-copy" variant - you can write the data directly as you compute it. + void *Push(size_t size, size_t *bindOffset) { + size_t off = Allocate(size); + *bindOffset = off; + return writePtr_ + off; + } + + VkBuffer GetVkBuffer() const { return buffer_; } + +private: + VkDeviceMemory deviceMemory_; + VkBuffer buffer_; + size_t offset_; + size_t size_; + uint8_t *writePtr_; +}; diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index f756364f2..673fdb4f5 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -33,6 +33,9 @@ #include "GPU/GPUState.h" #include "GPU/ge_constants.h" +#include "Common/Vulkan/VulkanContext.h" +#include "Common/Vulkan/VulkanMemory.h" + #include "GPU/Common/TextureDecoder.h" #include "GPU/Common/SplineCommon.h" #include "GPU/Common/TransformCommon.h" diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 6bdd619be..13972b6c9 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -27,6 +27,7 @@ #include "math/dataconv.h" #include "util/text/utf8.h" #include "Common/Vulkan/VulkanContext.h" +#include "Common/Vulkan/VulkanMemory.h" #include "Common/Common.h" #include "Core/Config.h" #include "Core/Reporting.h" diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index dc34d05ab..4d5eb0003 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -31,6 +31,7 @@ #include "Common/Vulkan/VulkanContext.h" #include "Common/Vulkan/VulkanImage.h" +#include "Common/Vulkan/VulkanMemory.h" #include "GPU/ge_constants.h" #include "GPU/GPUState.h" diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index 0d256ba3a..8a4998aa3 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -34,6 +34,7 @@ class DrawEngineVulkan; class VulkanContext; class VulkanTexture; +class VulkanPushBuffer; struct SamplerCacheKey { SamplerCacheKey() : fullKey(0) {} diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 45d2f9e0f..3e8e1ddc4 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -128,6 +128,7 @@ VULKAN_FILES := \ $(SRC)/Common/Vulkan/VulkanLoader.cpp \ $(SRC)/Common/Vulkan/VulkanContext.cpp \ $(SRC)/Common/Vulkan/VulkanImage.cpp \ + $(SRC)/Common/Vulkan/VulkanMemory.cpp \ $(SRC)/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp \ $(SRC)/GPU/Vulkan/DrawEngineVulkan.cpp \ $(SRC)/GPU/Vulkan/FramebufferVulkan.cpp \ diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 6a9585c53..9d1397a78 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -33,6 +33,7 @@ #include "Common/Vulkan/VulkanContext.h" #include "Common/Vulkan/VulkanImage.h" +#include "Common/Vulkan/VulkanMemory.h" // We use a simple descriptor set for all rendering: 1 sampler, 1 texture, 1 UBO binding point. // binding 0 - uniform data