mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-15 10:40:01 +01:00
Merge branch 'master' into TemporaryHackishBranch
# Conflicts: # GPU/Common/ShaderUniforms.h # GPU/Directx9/ShaderManagerDX9.cpp # GPU/Directx9/VertexShaderGeneratorDX9.cpp # GPU/Directx9/VertexShaderGeneratorDX9.h # GPU/GLES/FramebufferManagerGLES.cpp # GPU/GLES/ShaderManagerGLES.cpp # GPU/GLES/TextureCacheGLES.cpp # UI/EmuScreen.cpp # UI/GameSettingsScreen.cpp # headless/Headless.cpp
This commit is contained in:
commit
fe2060bfe3
257 files changed
+22555
-19976
No files matched your search
+24
-5
@@ -113,7 +113,9 @@ option(SIMULATOR "Set to ON when targeting an x86 simulator of an ARM platform"
|
||||
# :: Options
|
||||
option(USE_FFMPEG "Build with FFMPEG support" ${USE_FFMPEG})
|
||||
option(USE_SYSTEM_FFMPEG "Dynamically link against system FFMPEG" ${USE_SYSTEM_FFMPEG})
|
||||
option(USE_SYSTEM_LIBZIP "Dynamically link against system libzip" ${USE_SYSTEM_LIBZIP})
|
||||
option(USE_WAYLAND_WSI "Set to ON to require Wayland support for Vulkan" ${USE_WAYLAND_WSI})
|
||||
option(USE_ADDRESS_SANITIZER "Use Clang memory sanitizer" ${USE_ADDRESS_SANITIZER})
|
||||
|
||||
if(UNIX AND NOT (APPLE OR ANDROID) AND VULKAN)
|
||||
add_definitions(-DVK_USE_PLATFORM_XLIB_KHR)
|
||||
@@ -214,6 +216,13 @@ if(NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-switch -Wno-uninitialized")
|
||||
endif()
|
||||
|
||||
if(USE_ADDRESS_SANITIZER)
|
||||
message("Address sanitizer enabled (DEBUG only)")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -fsanitize=address")
|
||||
set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fsanitize=address")
|
||||
add_definitions(-DUSE_ADDRESS_SANITIZER)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -D_DEBUG")
|
||||
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -Os -D_NDEBUG")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -D_NDEBUG")
|
||||
@@ -538,7 +547,7 @@ endif()
|
||||
|
||||
|
||||
find_package(LibZip)
|
||||
if(LIBZIP_FOUND)
|
||||
if(LIBZIP_FOUND AND USE_SYSTEM_LIBZIP)
|
||||
add_definitions(-DSHARED_LIBZIP)
|
||||
else()
|
||||
add_library(libzip STATIC
|
||||
@@ -724,6 +733,8 @@ elseif(USING_QT_UI)
|
||||
${QT_UI_GEN}
|
||||
Qt/QtMain.cpp
|
||||
Qt/QtMain.h
|
||||
Qt/QtHost.cpp
|
||||
Qt/QtHost.h
|
||||
Qt/mainwindow.cpp
|
||||
Qt/mainwindow.h
|
||||
Qt/Debugger/ctrldisasmview.cpp
|
||||
@@ -768,7 +779,9 @@ elseif(TARGET SDL2::SDL2)
|
||||
set(nativeExtra ${nativeExtra}
|
||||
SDL/SDLJoystick.h
|
||||
SDL/SDLJoystick.cpp
|
||||
SDL/SDLMain.cpp)
|
||||
SDL/SDLMain.cpp
|
||||
SDL/SDLGLGraphicsContext.cpp
|
||||
SDL/SDLVulkanGraphicsContext.cpp)
|
||||
set(nativeExtraLibs ${nativeExtraLibs} SDL2::SDL2)
|
||||
if(APPLE)
|
||||
set(nativeExtra ${nativeExtra} SDL/SDLMain.h SDL/SDLMain.mm)
|
||||
@@ -830,7 +843,14 @@ if(ANDROID)
|
||||
set(nativeExtra ${nativeExtra} ${NativeAppSource})
|
||||
endif()
|
||||
|
||||
set(THIN3D_PLATFORMS ext/native/thin3d/thin3d_gl.cpp)
|
||||
set(THIN3D_PLATFORMS ext/native/thin3d/thin3d_gl.cpp
|
||||
ext/native/thin3d/GLRenderManager.cpp
|
||||
ext/native/thin3d/GLRenderManager.h
|
||||
ext/native/thin3d/GLQueueRunner.cpp
|
||||
ext/native/thin3d/GLQueueRunner.h
|
||||
ext/native/thin3d/DataFormatGL.cpp
|
||||
ext/native/thin3d/DataFormatGL.h)
|
||||
|
||||
set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS}
|
||||
ext/native/thin3d/thin3d_vulkan.cpp
|
||||
ext/native/thin3d/VulkanRenderManager.cpp
|
||||
@@ -887,8 +907,6 @@ add_library(native STATIC
|
||||
ext/native/file/vfs.h
|
||||
ext/native/file/zip_read.cpp
|
||||
ext/native/file/zip_read.h
|
||||
ext/native/gfx/GLStateCache.cpp
|
||||
ext/native/gfx/GLStateCache.h
|
||||
ext/native/gfx/gl_common.h
|
||||
ext/native/gfx/gl_debug_log.cpp
|
||||
ext/native/gfx/gl_debug_log.h
|
||||
@@ -964,6 +982,7 @@ add_library(native STATIC
|
||||
ext/native/profiler/profiler.h
|
||||
ext/native/thin3d/thin3d.cpp
|
||||
ext/native/thin3d/thin3d.h
|
||||
ext/native/thin3d/thin3d_create.h
|
||||
${THIN3D_PLATFORMS}
|
||||
ext/native/thread/executor.cpp
|
||||
ext/native/thread/executor.h
|
||||
|
||||
@@ -327,9 +327,7 @@ void ARM64XEmitter::FlushIcacheSection(u8* start, u8* end)
|
||||
// Header file says this is equivalent to: sys_icache_invalidate(start, end - start);
|
||||
sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start);
|
||||
#elif PPSSPP_PLATFORM(WINDOWS)
|
||||
#if !PPSSPP_PLATFORM(UWP) // Not available on UWP, which is very bad!
|
||||
FlushInstructionCache(GetCurrentProcess(), start, end - start);
|
||||
#endif
|
||||
#elif PPSSPP_ARCH(ARM64)
|
||||
// Code from Dolphin, contributed by the Mono project.
|
||||
|
||||
|
||||
@@ -633,9 +633,7 @@ void ARMXEmitter::FlushIcacheSection(u8 *start, u8 *end)
|
||||
// Header file says this is equivalent to: sys_icache_invalidate(start, end - start);
|
||||
sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start);
|
||||
#elif PPSSPP_PLATFORM(WINDOWS)
|
||||
#if !PPSSPP_PLATFORM(UWP) // Not available on UWP, which is very bad!
|
||||
FlushInstructionCache(GetCurrentProcess(), start, end - start);
|
||||
#endif
|
||||
#elif PPSSPP_ARCH(ARM)
|
||||
|
||||
#if defined(__clang__) || defined(__ANDROID__)
|
||||
|
||||
@@ -14,4 +14,5 @@
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
#endif
|
||||
#undef DrawText
|
||||
#endif
|
||||
+2
-3
@@ -241,10 +241,9 @@ bool CreateDir(const std::string &path)
|
||||
bool CreateFullPath(const std::string &fullPath)
|
||||
{
|
||||
int panicCounter = 100;
|
||||
DEBUG_LOG(COMMON, "CreateFullPath: path %s", fullPath.c_str());
|
||||
VERBOSE_LOG(COMMON, "CreateFullPath: path %s", fullPath.c_str());
|
||||
|
||||
if (File::Exists(fullPath))
|
||||
{
|
||||
if (File::Exists(fullPath)) {
|
||||
DEBUG_LOG(COMMON, "CreateFullPath: path exists %s", fullPath.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ class GraphicsContext {
|
||||
public:
|
||||
virtual ~GraphicsContext() {}
|
||||
|
||||
virtual bool InitFromRenderThread(std::string *errorMessage) { return true; }
|
||||
virtual void ShutdownFromRenderThread() {}
|
||||
|
||||
virtual void Shutdown() = 0;
|
||||
virtual void SwapInterval(int interval) = 0;
|
||||
|
||||
@@ -25,6 +28,12 @@ public:
|
||||
// Needs casting to the appropriate type, unfortunately. Should find a better solution..
|
||||
virtual void *GetAPIContext() { return nullptr; }
|
||||
|
||||
// Called from the render thread from threaded backends.
|
||||
virtual void ThreadStart() {}
|
||||
virtual bool ThreadFrame() { return true; }
|
||||
virtual void ThreadEnd() {}
|
||||
virtual void StopThread() {}
|
||||
|
||||
virtual Draw::DrawContext *GetDrawContext() = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ void MemArena::ReleaseView(void* view, size_t size) {
|
||||
|
||||
u8* MemArena::Find4GBBase() {
|
||||
// Now, create views in high memory where there's plenty of space.
|
||||
#if PPSSPP_ARCH(64BIT)
|
||||
#if PPSSPP_ARCH(64BIT) && !defined(USE_ADDRESS_SANITIZER)
|
||||
// Very precarious - mmap cannot return an error when trying to map already used pages.
|
||||
// This makes the Windows approach above unusable on Linux, so we will simply pray...
|
||||
return reinterpret_cast<u8*>(0x2300000000ULL);
|
||||
|
||||
@@ -547,7 +547,7 @@ VkResult VulkanContext::CreateDevice() {
|
||||
return VK_ERROR_INITIALIZATION_FAILED;
|
||||
}
|
||||
|
||||
VkDeviceQueueCreateInfo queue_info = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };
|
||||
VkDeviceQueueCreateInfo queue_info{ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };
|
||||
float queue_priorities[1] = { 1.0f };
|
||||
queue_info.queueCount = 1;
|
||||
queue_info.pQueuePriorities = queue_priorities;
|
||||
@@ -563,7 +563,7 @@ VkResult VulkanContext::CreateDevice() {
|
||||
|
||||
deviceExtensionsLookup_.DEDICATED_ALLOCATION = EnableDeviceExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
|
||||
|
||||
VkDeviceCreateInfo device_info { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
|
||||
VkDeviceCreateInfo device_info{ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
|
||||
device_info.queueCreateInfoCount = 1;
|
||||
device_info.pQueueCreateInfos = &queue_info;
|
||||
device_info.enabledLayerCount = (uint32_t)device_layer_names_.size();
|
||||
@@ -579,6 +579,7 @@ VkResult VulkanContext::CreateDevice() {
|
||||
VulkanLoadDeviceFunctions(device_);
|
||||
}
|
||||
ILOG("Device created.\n");
|
||||
VulkanSetAvailable(true);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -922,29 +923,21 @@ VkFence VulkanContext::CreateFence(bool presignalled) {
|
||||
return fence;
|
||||
}
|
||||
|
||||
void VulkanContext::DestroyDevice() {
|
||||
ILOG("VulkanContext::DestroyDevice (performing deletes)");
|
||||
// If there happen to be any pending deletes, now is a good time.
|
||||
void VulkanContext::PerformPendingDeletes() {
|
||||
for (int i = 0; i < ARRAY_SIZE(frame_); i++) {
|
||||
frame_[i].deleteList.PerformDeletes(device_);
|
||||
}
|
||||
Delete().PerformDeletes(device_);
|
||||
}
|
||||
|
||||
void VulkanContext::DestroyDevice() {
|
||||
ILOG("VulkanContext::DestroyDevice (performing deletes)");
|
||||
PerformPendingDeletes();
|
||||
|
||||
vkDestroyDevice(device_, nullptr);
|
||||
device_ = nullptr;
|
||||
}
|
||||
|
||||
VkPipelineCache VulkanContext::CreatePipelineCache() {
|
||||
VkPipelineCache cache;
|
||||
VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
|
||||
pc.pInitialData = nullptr;
|
||||
pc.initialDataSize = 0;
|
||||
pc.flags = 0;
|
||||
VkResult res = vkCreatePipelineCache(device_, &pc, nullptr, &cache);
|
||||
assert(VK_SUCCESS == res);
|
||||
return cache;
|
||||
}
|
||||
|
||||
bool VulkanContext::CreateShaderModule(const std::vector<uint32_t> &spirv, VkShaderModule *shaderModule) {
|
||||
VkShaderModuleCreateInfo sm{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
|
||||
sm.pCode = spirv.data();
|
||||
@@ -962,6 +955,24 @@ void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, int baseMip, int
|
||||
VkImageLayout oldImageLayout, VkImageLayout newImageLayout,
|
||||
VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
|
||||
VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask) {
|
||||
#ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_COLOR
|
||||
if (aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) {
|
||||
// Hack to disable transaction elimination on ARM Mali.
|
||||
if (oldImageLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL || oldImageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
|
||||
oldImageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
if (newImageLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL || newImageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
|
||||
newImageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
}
|
||||
#endif
|
||||
#ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_DEPTH_STENCIL
|
||||
if (aspectMask != VK_IMAGE_ASPECT_COLOR_BIT) {
|
||||
// Hack to disable transaction elimination on ARM Mali.
|
||||
if (oldImageLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL || oldImageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
|
||||
oldImageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
if (newImageLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL || newImageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
|
||||
newImageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
}
|
||||
#endif
|
||||
VkImageMemoryBarrier image_memory_barrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
|
||||
image_memory_barrier.srcAccessMask = srcAccessMask;
|
||||
image_memory_barrier.dstAccessMask = dstAccessMask;
|
||||
@@ -1090,22 +1101,22 @@ const char *VulkanResultToString(VkResult res) {
|
||||
}
|
||||
|
||||
void VulkanDeleteList::Take(VulkanDeleteList &del) {
|
||||
assert(cmdPools_.size() == 0);
|
||||
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(pipelines_.size() == 0);
|
||||
assert(pipelineCaches_.size() == 0);
|
||||
assert(renderPasses_.size() == 0);
|
||||
assert(framebuffers_.size() == 0);
|
||||
assert(pipelineLayouts_.size() == 0);
|
||||
assert(descSetLayouts_.size() == 0);
|
||||
assert(callbacks_.size() == 0);
|
||||
assert(cmdPools_.empty());
|
||||
assert(descPools_.empty());
|
||||
assert(modules_.empty());
|
||||
assert(buffers_.empty());
|
||||
assert(bufferViews_.empty());
|
||||
assert(images_.empty());
|
||||
assert(imageViews_.empty());
|
||||
assert(deviceMemory_.empty());
|
||||
assert(samplers_.empty());
|
||||
assert(pipelines_.empty());
|
||||
assert(pipelineCaches_.empty());
|
||||
assert(renderPasses_.empty());
|
||||
assert(framebuffers_.empty());
|
||||
assert(pipelineLayouts_.empty());
|
||||
assert(descSetLayouts_.empty());
|
||||
assert(callbacks_.empty());
|
||||
cmdPools_ = std::move(del.cmdPools_);
|
||||
descPools_ = std::move(del.descPools_);
|
||||
modules_ = std::move(del.modules_);
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
#include "base/logging.h"
|
||||
#include "Common/Vulkan/VulkanLoader.h"
|
||||
|
||||
// Amount of time, in nanoseconds, to wait for a command buffer to complete
|
||||
#define FENCE_TIMEOUT 10000000000
|
||||
|
||||
enum {
|
||||
VULKAN_FLAG_VALIDATE = 1,
|
||||
VULKAN_FLAG_PRESENT_MAILBOX = 2,
|
||||
@@ -109,6 +106,12 @@ struct VulkanDeviceExtensions {
|
||||
bool DEDICATED_ALLOCATION;
|
||||
};
|
||||
|
||||
// Useful for debugging on ARM Mali. This eliminates transaction elimination
|
||||
// which can cause artifacts if you get barriers wrong (or if there are driver bugs).
|
||||
// Cost is reduced performance on some GPU architectures.
|
||||
// #define VULKAN_USE_GENERAL_LAYOUT_FOR_COLOR
|
||||
// #define VULKAN_USE_GENERAL_LAYOUT_FOR_DEPTH_STENCIL
|
||||
|
||||
// VulkanContext manages the device and swapchain, and deferred deletion of objects.
|
||||
class VulkanContext {
|
||||
public:
|
||||
@@ -129,15 +132,14 @@ public:
|
||||
bool EnableDeviceExtension(const char *extension);
|
||||
VkResult CreateDevice();
|
||||
|
||||
const std::string &InitError() { return init_error_; }
|
||||
const std::string &InitError() const { return init_error_; }
|
||||
|
||||
VkDevice GetDevice() { return device_; }
|
||||
VkInstance GetInstance() { return instance_; }
|
||||
VkDevice GetDevice() const { return device_; }
|
||||
VkInstance GetInstance() const { return instance_; }
|
||||
uint32_t GetFlags() const { return flags_; }
|
||||
|
||||
VulkanDeleteList &Delete() { return globalDeleteList_; }
|
||||
|
||||
VkPipelineCache CreatePipelineCache();
|
||||
|
||||
// The parameters are whatever the chosen window system wants.
|
||||
void InitSurface(WindowSystem winsys, void *data1, void *data2, int width = -1, int height = -1);
|
||||
void ReinitSurface(int width = -1, int height = -1);
|
||||
@@ -148,9 +150,9 @@ public:
|
||||
|
||||
// Also destroys the surface.
|
||||
void DestroyObjects();
|
||||
|
||||
void DestroyDevice();
|
||||
|
||||
void PerformPendingDeletes();
|
||||
void WaitUntilQueueIdle();
|
||||
|
||||
// Utility functions for shorter code
|
||||
|
||||
+62
-255
@@ -2,216 +2,18 @@
|
||||
#include "Common/Vulkan/VulkanMemory.h"
|
||||
#include "Common/Log.h"
|
||||
|
||||
VkResult VulkanTexture::Create(int w, int h, VkFormat format) {
|
||||
tex_width = w;
|
||||
tex_height = h;
|
||||
format_ = format;
|
||||
|
||||
VkFormatProperties formatProps;
|
||||
vkGetPhysicalDeviceFormatProperties(vulkan_->GetPhysicalDevice(), format, &formatProps);
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
void VulkanTexture::CreateMappableImage() {
|
||||
// If we already have a mappableImage, forget it.
|
||||
if (mappableImage) {
|
||||
vulkan_->Delete().QueueDeleteImage(mappableImage);
|
||||
}
|
||||
if (mappableMemory) {
|
||||
vulkan_->Delete().QueueDeleteDeviceMemory(mappableMemory);
|
||||
}
|
||||
|
||||
VkImageCreateInfo image_create_info{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
|
||||
image_create_info.imageType = VK_IMAGE_TYPE_2D;
|
||||
image_create_info.format = format_;
|
||||
image_create_info.extent.width = tex_width;
|
||||
image_create_info.extent.height = tex_height;
|
||||
image_create_info.extent.depth = 1;
|
||||
image_create_info.mipLevels = 1;
|
||||
image_create_info.arrayLayers = 1;
|
||||
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
image_create_info.tiling = VK_IMAGE_TILING_LINEAR;
|
||||
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
image_create_info.queueFamilyIndexCount = 0;
|
||||
image_create_info.pQueueFamilyIndices = NULL;
|
||||
image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
image_create_info.flags = 0;
|
||||
image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
||||
|
||||
VkMemoryAllocateInfo mem_alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
||||
mem_alloc.allocationSize = 0;
|
||||
mem_alloc.memoryTypeIndex = 0;
|
||||
|
||||
// Create a mappable image. It will be the texture if linear images are ok to be textures
|
||||
// or it will be the staging image if they are not.
|
||||
VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &mappableImage);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
vkGetImageMemoryRequirements(vulkan_->GetDevice(), mappableImage, &mem_reqs);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
mem_alloc.allocationSize = mem_reqs.size;
|
||||
|
||||
// Find the memory type that is host mappable.
|
||||
bool pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &mem_alloc.memoryTypeIndex);
|
||||
assert(pass);
|
||||
|
||||
res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mappableMemory);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
res = vkBindImageMemory(vulkan_->GetDevice(), mappableImage, mappableMemory, 0);
|
||||
assert(res == VK_SUCCESS);
|
||||
}
|
||||
|
||||
uint8_t *VulkanTexture::Lock(int level, int *rowPitch) {
|
||||
CreateMappableImage();
|
||||
|
||||
VkImageSubresource subres = {};
|
||||
subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
subres.mipLevel = 0;
|
||||
subres.arrayLayer = 0;
|
||||
|
||||
VkSubresourceLayout layout;
|
||||
void *data;
|
||||
|
||||
// Get the subresource layout so we know what the row pitch is
|
||||
vkGetImageSubresourceLayout(vulkan_->GetDevice(), mappableImage, &subres, &layout);
|
||||
VkResult res = vkMapMemory(vulkan_->GetDevice(), mappableMemory, layout.offset, layout.size, 0, &data);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
*rowPitch = (int)layout.rowPitch;
|
||||
return (uint8_t *)data;
|
||||
}
|
||||
|
||||
void VulkanTexture::Unlock(VkCommandBuffer cmd) {
|
||||
vkUnmapMemory(vulkan_->GetDevice(), mappableMemory);
|
||||
|
||||
// if we already have an image, queue it for destruction and forget it.
|
||||
Wipe();
|
||||
|
||||
{ // Shrink the diff by not unindenting. If you make major changes, remove this.
|
||||
VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
|
||||
image_create_info.imageType = VK_IMAGE_TYPE_2D;
|
||||
image_create_info.format = format_;
|
||||
image_create_info.extent.width = tex_width;
|
||||
image_create_info.extent.height = tex_height;
|
||||
image_create_info.extent.depth = 1;
|
||||
image_create_info.mipLevels = 1;
|
||||
image_create_info.arrayLayers = 1;
|
||||
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
image_create_info.queueFamilyIndexCount = 0;
|
||||
image_create_info.pQueueFamilyIndices = NULL;
|
||||
image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
image_create_info.flags = 0;
|
||||
// The mappable image cannot be our texture, so create an optimally tiled image and blit to it
|
||||
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &image);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
vkGetImageMemoryRequirements(vulkan_->GetDevice(), image, &mem_reqs);
|
||||
|
||||
VkMemoryAllocateInfo mem_alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
||||
mem_alloc.memoryTypeIndex = 0;
|
||||
mem_alloc.allocationSize = mem_reqs.size;
|
||||
|
||||
if (allocator_) {
|
||||
offset_ = allocator_->Allocate(mem_reqs, &mem);
|
||||
} else {
|
||||
offset_ = 0;
|
||||
// Find memory type - don't specify any mapping requirements
|
||||
bool pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &mem_alloc.memoryTypeIndex);
|
||||
assert(pass);
|
||||
res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mem);
|
||||
assert(res == VK_SUCCESS);
|
||||
}
|
||||
|
||||
res = vkBindImageMemory(vulkan_->GetDevice(), image, mem, offset_);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
// Since we're going to blit from the mappable image, set its layout to SOURCE_OPTIMAL
|
||||
TransitionImageLayout2(cmd, mappableImage, 0, 1,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
|
||||
|
||||
TransitionImageLayout2(cmd, image, 0, 1,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
0, VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||
|
||||
VkImageCopy copy_region;
|
||||
copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
copy_region.srcSubresource.mipLevel = 0;
|
||||
copy_region.srcSubresource.baseArrayLayer = 0;
|
||||
copy_region.srcSubresource.layerCount = 1;
|
||||
copy_region.srcOffset.x = 0;
|
||||
copy_region.srcOffset.y = 0;
|
||||
copy_region.srcOffset.z = 0;
|
||||
copy_region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
copy_region.dstSubresource.mipLevel = 0;
|
||||
copy_region.dstSubresource.baseArrayLayer = 0;
|
||||
copy_region.dstSubresource.layerCount = 1;
|
||||
copy_region.dstOffset.x = 0;
|
||||
copy_region.dstOffset.y = 0;
|
||||
copy_region.dstOffset.z = 0;
|
||||
copy_region.extent.width = tex_width;
|
||||
copy_region.extent.height = tex_height;
|
||||
copy_region.extent.depth = 1;
|
||||
|
||||
// Put the copy command into the command buffer
|
||||
vkCmdCopyImage(cmd,
|
||||
mappableImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1, ©_region);
|
||||
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
// Set the layout for the texture image from DESTINATION_OPTIMAL to SHADER_READ_ONLY
|
||||
TransitionImageLayout2(cmd, image, 0, 1,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT);
|
||||
|
||||
// Then drop the temporary mappable image - although should not be necessary...
|
||||
vulkan_->Delete().QueueDeleteImage(mappableImage);
|
||||
vulkan_->Delete().QueueDeleteDeviceMemory(mappableMemory);
|
||||
}
|
||||
|
||||
VkImageViewCreateInfo view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
|
||||
view_info.image = image;
|
||||
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
view_info.format = format_;
|
||||
view_info.components.r = VK_COMPONENT_SWIZZLE_R;
|
||||
view_info.components.g = VK_COMPONENT_SWIZZLE_G;
|
||||
view_info.components.b = VK_COMPONENT_SWIZZLE_B;
|
||||
view_info.components.a = VK_COMPONENT_SWIZZLE_A;
|
||||
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
view_info.subresourceRange.baseMipLevel = 0;
|
||||
view_info.subresourceRange.levelCount = 1;
|
||||
view_info.subresourceRange.baseArrayLayer = 0;
|
||||
view_info.subresourceRange.layerCount = 1;
|
||||
VkResult res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &view);
|
||||
assert(res == VK_SUCCESS);
|
||||
}
|
||||
|
||||
void VulkanTexture::Wipe() {
|
||||
if (image) {
|
||||
vulkan_->Delete().QueueDeleteImage(image);
|
||||
if (image_) {
|
||||
vulkan_->Delete().QueueDeleteImage(image_);
|
||||
}
|
||||
if (view) {
|
||||
vulkan_->Delete().QueueDeleteImageView(view);
|
||||
if (view_) {
|
||||
vulkan_->Delete().QueueDeleteImageView(view_);
|
||||
}
|
||||
if (mem && !allocator_) {
|
||||
vulkan_->Delete().QueueDeleteDeviceMemory(mem);
|
||||
} else if (mem) {
|
||||
allocator_->Free(mem, offset_);
|
||||
mem = VK_NULL_HANDLE;
|
||||
if (mem_ && !allocator_) {
|
||||
vulkan_->Delete().QueueDeleteDeviceMemory(mem_);
|
||||
} else if (mem_) {
|
||||
allocator_->Free(mem_, offset_);
|
||||
mem_ = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,18 +33,18 @@ static bool IsDepthStencilFormat(VkFormat format) {
|
||||
bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage, const VkComponentMapping *mapping) {
|
||||
Wipe();
|
||||
|
||||
tex_width = w;
|
||||
tex_height = h;
|
||||
width_ = w;
|
||||
height_ = h;
|
||||
numMips_ = numMips;
|
||||
format_ = format;
|
||||
|
||||
VkImageAspectFlags aspect = IsDepthStencilFormat(format) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
|
||||
VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
|
||||
VkImageCreateInfo image_create_info{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
|
||||
image_create_info.imageType = VK_IMAGE_TYPE_2D;
|
||||
image_create_info.format = format_;
|
||||
image_create_info.extent.width = tex_width;
|
||||
image_create_info.extent.height = tex_height;
|
||||
image_create_info.extent.width = width_;
|
||||
image_create_info.extent.height = height_;
|
||||
image_create_info.extent.depth = 1;
|
||||
image_create_info.mipLevels = numMips;
|
||||
image_create_info.arrayLayers = 1;
|
||||
@@ -256,41 +58,51 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips,
|
||||
image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
}
|
||||
|
||||
VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &image);
|
||||
// The graphics debugger always "needs" TRANSFER_SRC but in practice doesn't matter -
|
||||
// unless validation is on. So let's only force it on when being validated, for now.
|
||||
if (vulkan_->GetFlags() & VULKAN_FLAG_VALIDATE) {
|
||||
image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
|
||||
VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &image_);
|
||||
if (res != VK_SUCCESS) {
|
||||
assert(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS);
|
||||
_assert_(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS);
|
||||
return false;
|
||||
}
|
||||
|
||||
vkGetImageMemoryRequirements(vulkan_->GetDevice(), image, &mem_reqs);
|
||||
VkMemoryRequirements mem_reqs{};
|
||||
vkGetImageMemoryRequirements(vulkan_->GetDevice(), image_, &mem_reqs);
|
||||
|
||||
if (allocator_) {
|
||||
offset_ = allocator_->Allocate(mem_reqs, &mem);
|
||||
offset_ = allocator_->Allocate(mem_reqs, &mem_);
|
||||
if (offset_ == VulkanDeviceAllocator::ALLOCATE_FAILED) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
VkMemoryAllocateInfo mem_alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
||||
VkMemoryAllocateInfo mem_alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
||||
mem_alloc.memoryTypeIndex = 0;
|
||||
mem_alloc.allocationSize = mem_reqs.size;
|
||||
|
||||
// Find memory type - don't specify any mapping requirements
|
||||
bool pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &mem_alloc.memoryTypeIndex);
|
||||
assert(pass);
|
||||
_assert_(pass);
|
||||
|
||||
res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mem);
|
||||
res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mem_);
|
||||
if (res != VK_SUCCESS) {
|
||||
_assert_msg_(G3D, res != VK_ERROR_TOO_MANY_OBJECTS, "Too many Vulkan memory objects!");
|
||||
assert(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS);
|
||||
_assert_(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS);
|
||||
vkDestroyImage(vulkan_->GetDevice(), image_, nullptr);
|
||||
image_ = VK_NULL_HANDLE;
|
||||
return false;
|
||||
}
|
||||
|
||||
offset_ = 0;
|
||||
}
|
||||
|
||||
res = vkBindImageMemory(vulkan_->GetDevice(), image, mem, offset_);
|
||||
res = vkBindImageMemory(vulkan_->GetDevice(), image_, mem_, offset_);
|
||||
if (res != VK_SUCCESS) {
|
||||
assert(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS);
|
||||
// This leaks the image and memory. Should not really happen though...
|
||||
_assert_(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -298,22 +110,22 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips,
|
||||
if (initialLayout != VK_IMAGE_LAYOUT_UNDEFINED && initialLayout != VK_IMAGE_LAYOUT_PREINITIALIZED) {
|
||||
switch (initialLayout) {
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
|
||||
TransitionImageLayout2(cmd, image, 0, numMips, VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
TransitionImageLayout2(cmd, image_, 0, numMips, VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
0, VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||
break;
|
||||
default:
|
||||
// If you planned to use UploadMip, you want VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL. After the
|
||||
// upload, you can transition.
|
||||
assert(false);
|
||||
// upload, you can transition using EndCreate.
|
||||
_assert_(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the view while we're at it.
|
||||
VkImageViewCreateInfo view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
|
||||
view_info.image = image;
|
||||
VkImageViewCreateInfo view_info{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
|
||||
view_info.image = image_;
|
||||
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
view_info.format = format_;
|
||||
if (mapping) {
|
||||
@@ -330,16 +142,17 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips,
|
||||
view_info.subresourceRange.baseArrayLayer = 0;
|
||||
view_info.subresourceRange.layerCount = 1;
|
||||
|
||||
res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &view);
|
||||
res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &view_);
|
||||
if (res != VK_SUCCESS) {
|
||||
assert(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS);
|
||||
// This leaks the image.
|
||||
_assert_(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanTexture::UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength) {
|
||||
VkBufferImageCopy copy_region = {};
|
||||
VkBufferImageCopy copy_region{};
|
||||
copy_region.bufferOffset = offset;
|
||||
copy_region.bufferRowLength = (uint32_t)rowLength;
|
||||
copy_region.bufferImageHeight = 0; // 2D
|
||||
@@ -351,7 +164,7 @@ void VulkanTexture::UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mi
|
||||
copy_region.imageSubresource.baseArrayLayer = 0;
|
||||
copy_region.imageSubresource.layerCount = 1;
|
||||
|
||||
vkCmdCopyBufferToImage(cmd, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region);
|
||||
vkCmdCopyBufferToImage(cmd, buffer, image_, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region);
|
||||
}
|
||||
|
||||
void VulkanTexture::GenerateMip(VkCommandBuffer cmd, int mip) {
|
||||
@@ -361,33 +174,33 @@ void VulkanTexture::GenerateMip(VkCommandBuffer cmd, int mip) {
|
||||
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
blit.srcSubresource.layerCount = 1;
|
||||
blit.srcSubresource.mipLevel = mip - 1;
|
||||
blit.srcOffsets[1].x = tex_width >> (mip - 1);
|
||||
blit.srcOffsets[1].y = tex_height >> (mip - 1);
|
||||
blit.srcOffsets[1].x = width_ >> (mip - 1);
|
||||
blit.srcOffsets[1].y = height_ >> (mip - 1);
|
||||
blit.srcOffsets[1].z = 1;
|
||||
|
||||
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
blit.dstSubresource.layerCount = 1;
|
||||
blit.dstSubresource.mipLevel = mip;
|
||||
blit.dstOffsets[1].x = tex_width >> mip;
|
||||
blit.dstOffsets[1].y = tex_height >> mip;
|
||||
blit.dstOffsets[1].x = width_ >> mip;
|
||||
blit.dstOffsets[1].y = height_ >> mip;
|
||||
blit.dstOffsets[1].z = 1;
|
||||
|
||||
TransitionImageLayout2(cmd, image, mip - 1, 1, VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
TransitionImageLayout2(cmd, image_, mip - 1, 1, VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
|
||||
|
||||
// Low-quality mipmap generation, but works okay.
|
||||
vkCmdBlitImage(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR);
|
||||
vkCmdBlitImage(cmd, image_, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image_, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR);
|
||||
|
||||
TransitionImageLayout2(cmd, image, mip - 1, 1, VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
TransitionImageLayout2(cmd, image_, mip - 1, 1, VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||
}
|
||||
|
||||
void VulkanTexture::EndCreate(VkCommandBuffer cmd, bool vertexTexture) {
|
||||
TransitionImageLayout2(cmd, image, 0, 1,
|
||||
TransitionImageLayout2(cmd, image_, 0, 1,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, vertexTexture ? VK_PIPELINE_STAGE_VERTEX_SHADER_BIT : VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
@@ -395,22 +208,16 @@ void VulkanTexture::EndCreate(VkCommandBuffer cmd, bool vertexTexture) {
|
||||
}
|
||||
|
||||
void VulkanTexture::Destroy() {
|
||||
if (view != VK_NULL_HANDLE) {
|
||||
vulkan_->Delete().QueueDeleteImageView(view);
|
||||
if (view_ != VK_NULL_HANDLE) {
|
||||
vulkan_->Delete().QueueDeleteImageView(view_);
|
||||
}
|
||||
if (image != VK_NULL_HANDLE) {
|
||||
if (mappableImage == image) {
|
||||
mappableImage = VK_NULL_HANDLE;
|
||||
}
|
||||
vulkan_->Delete().QueueDeleteImage(image);
|
||||
if (image_ != VK_NULL_HANDLE) {
|
||||
vulkan_->Delete().QueueDeleteImage(image_);
|
||||
}
|
||||
if (mem != VK_NULL_HANDLE && !allocator_) {
|
||||
if (mappableMemory == mem) {
|
||||
mappableMemory = VK_NULL_HANDLE;
|
||||
}
|
||||
vulkan_->Delete().QueueDeleteDeviceMemory(mem);
|
||||
} else if (mem != VK_NULL_HANDLE) {
|
||||
allocator_->Free(mem, offset_);
|
||||
mem = VK_NULL_HANDLE;
|
||||
if (mem_ != VK_NULL_HANDLE && !allocator_) {
|
||||
vulkan_->Delete().QueueDeleteDeviceMemory(mem_);
|
||||
} else if (mem_ != VK_NULL_HANDLE) {
|
||||
allocator_->Free(mem_, offset_);
|
||||
mem_ = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
+13
-28
@@ -9,25 +9,12 @@ class VulkanDeviceAllocator;
|
||||
class VulkanTexture {
|
||||
public:
|
||||
VulkanTexture(VulkanContext *vulkan, VulkanDeviceAllocator *allocator)
|
||||
: vulkan_(vulkan), image(VK_NULL_HANDLE), mem(VK_NULL_HANDLE), view(VK_NULL_HANDLE),
|
||||
tex_width(0), tex_height(0), numMips_(1), format_(VK_FORMAT_UNDEFINED),
|
||||
mappableImage(VK_NULL_HANDLE), mappableMemory(VK_NULL_HANDLE),
|
||||
allocator_(allocator), offset_(0) {
|
||||
memset(&mem_reqs, 0, sizeof(mem_reqs));
|
||||
: vulkan_(vulkan), allocator_(allocator) {
|
||||
}
|
||||
~VulkanTexture() {
|
||||
Destroy();
|
||||
}
|
||||
|
||||
// Simple usage - no cleverness, no mipmaps.
|
||||
// Always call Create, Lock, Unlock. Unlock performs the upload if necessary.
|
||||
// Can later Lock and Unlock again. This cannot change the format. Create cannot
|
||||
// be called a second time without recreating the texture object until Destroy has
|
||||
// been called.
|
||||
VkResult Create(int w, int h, VkFormat format);
|
||||
uint8_t *Lock(int level, int *rowPitch);
|
||||
void Unlock(VkCommandBuffer cmd);
|
||||
|
||||
// Fast uploads from buffer. Mipmaps supported.
|
||||
// Usage must at least include VK_IMAGE_USAGE_TRANSFER_DST_BIT in order to use UploadMip.
|
||||
// When using UploadMip, initialLayout should be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL.
|
||||
@@ -39,29 +26,27 @@ public:
|
||||
void Destroy();
|
||||
|
||||
// Used in image copies, etc.
|
||||
VkImage GetImage() const { return image; }
|
||||
VkImage GetImage() const { return image_; }
|
||||
|
||||
// Used for sampling, generally.
|
||||
VkImageView GetImageView() const { return view; }
|
||||
VkImageView GetImageView() const { return view_; }
|
||||
|
||||
int32_t GetWidth() const { return tex_width; }
|
||||
int32_t GetHeight() const { return tex_height; }
|
||||
int32_t GetWidth() const { return width_; }
|
||||
int32_t GetHeight() const { return height_; }
|
||||
int32_t GetNumMips() const { return numMips_; }
|
||||
VkFormat GetFormat() const { return format_; }
|
||||
|
||||
private:
|
||||
void CreateMappableImage();
|
||||
void Wipe();
|
||||
|
||||
VulkanContext *vulkan_;
|
||||
VkImage image;
|
||||
VkDeviceMemory mem;
|
||||
VkImageView view;
|
||||
int32_t tex_width, tex_height, numMips_;
|
||||
VkFormat format_;
|
||||
VkImage mappableImage;
|
||||
VkDeviceMemory mappableMemory;
|
||||
VkMemoryRequirements mem_reqs;
|
||||
VkImage image_ = VK_NULL_HANDLE;
|
||||
VkImageView view_ = VK_NULL_HANDLE;
|
||||
VkDeviceMemory mem_ = VK_NULL_HANDLE;
|
||||
int32_t width_ = 0;
|
||||
int32_t height_ = 0;
|
||||
int32_t numMips_ = 1;
|
||||
VkFormat format_ = VK_FORMAT_UNDEFINED;
|
||||
VulkanDeviceAllocator *allocator_;
|
||||
size_t offset_;
|
||||
size_t offset_ = 0;
|
||||
};
|
||||
+140
-29
@@ -16,6 +16,7 @@
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "Common/Vulkan/VulkanLoader.h"
|
||||
#include <vector>
|
||||
#include "base/logging.h"
|
||||
#include "base/basictypes.h"
|
||||
|
||||
@@ -113,16 +114,12 @@ PFN_vkDestroyCommandPool vkDestroyCommandPool;
|
||||
PFN_vkResetCommandPool vkResetCommandPool;
|
||||
PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers;
|
||||
PFN_vkFreeCommandBuffers vkFreeCommandBuffers;
|
||||
PFN_vkBeginCommandBuffer vkBeginCommandBuffer;
|
||||
PFN_vkEndCommandBuffer vkEndCommandBuffer;
|
||||
PFN_vkResetCommandBuffer vkResetCommandBuffer;
|
||||
|
||||
// Used frequently
|
||||
PFN_vkCmdBindPipeline vkCmdBindPipeline;
|
||||
PFN_vkCmdSetViewport vkCmdSetViewport;
|
||||
PFN_vkCmdSetScissor vkCmdSetScissor;
|
||||
PFN_vkCmdSetLineWidth vkCmdSetLineWidth;
|
||||
PFN_vkCmdSetDepthBias vkCmdSetDepthBias;
|
||||
PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants;
|
||||
PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds;
|
||||
PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask;
|
||||
PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask;
|
||||
PFN_vkCmdSetStencilReference vkCmdSetStencilReference;
|
||||
@@ -131,34 +128,43 @@ PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer;
|
||||
PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers;
|
||||
PFN_vkCmdDraw vkCmdDraw;
|
||||
PFN_vkCmdDrawIndexed vkCmdDrawIndexed;
|
||||
PFN_vkCmdDrawIndirect vkCmdDrawIndirect;
|
||||
PFN_vkCmdDrawIndexedIndirect vkCmdDrawIndexedIndirect;
|
||||
PFN_vkCmdDispatch vkCmdDispatch;
|
||||
PFN_vkCmdDispatchIndirect vkCmdDispatchIndirect;
|
||||
PFN_vkCmdClearAttachments vkCmdClearAttachments;
|
||||
PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier;
|
||||
PFN_vkCmdPushConstants vkCmdPushConstants;
|
||||
|
||||
// Every frame to a few times per frame
|
||||
PFN_vkBeginCommandBuffer vkBeginCommandBuffer;
|
||||
PFN_vkEndCommandBuffer vkEndCommandBuffer;
|
||||
PFN_vkResetCommandBuffer vkResetCommandBuffer;
|
||||
PFN_vkCmdSetEvent vkCmdSetEvent;
|
||||
PFN_vkCmdResetEvent vkCmdResetEvent;
|
||||
PFN_vkCmdWaitEvents vkCmdWaitEvents;
|
||||
PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass;
|
||||
PFN_vkCmdEndRenderPass vkCmdEndRenderPass;
|
||||
PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
|
||||
PFN_vkCmdCopyImage vkCmdCopyImage;
|
||||
PFN_vkCmdBlitImage vkCmdBlitImage;
|
||||
PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage;
|
||||
PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer;
|
||||
|
||||
PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds;
|
||||
PFN_vkCmdSetLineWidth vkCmdSetLineWidth;
|
||||
PFN_vkCmdSetDepthBias vkCmdSetDepthBias;
|
||||
PFN_vkCmdDrawIndirect vkCmdDrawIndirect;
|
||||
PFN_vkCmdDrawIndexedIndirect vkCmdDrawIndexedIndirect;
|
||||
PFN_vkCmdDispatch vkCmdDispatch;
|
||||
PFN_vkCmdDispatchIndirect vkCmdDispatchIndirect;
|
||||
PFN_vkCmdUpdateBuffer vkCmdUpdateBuffer;
|
||||
PFN_vkCmdFillBuffer vkCmdFillBuffer;
|
||||
PFN_vkCmdClearColorImage vkCmdClearColorImage;
|
||||
PFN_vkCmdClearDepthStencilImage vkCmdClearDepthStencilImage;
|
||||
PFN_vkCmdClearAttachments vkCmdClearAttachments;
|
||||
PFN_vkCmdResolveImage vkCmdResolveImage;
|
||||
PFN_vkCmdSetEvent vkCmdSetEvent;
|
||||
PFN_vkCmdResetEvent vkCmdResetEvent;
|
||||
PFN_vkCmdWaitEvents vkCmdWaitEvents;
|
||||
PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier;
|
||||
PFN_vkCmdBeginQuery vkCmdBeginQuery;
|
||||
PFN_vkCmdEndQuery vkCmdEndQuery;
|
||||
PFN_vkCmdResetQueryPool vkCmdResetQueryPool;
|
||||
PFN_vkCmdWriteTimestamp vkCmdWriteTimestamp;
|
||||
PFN_vkCmdCopyQueryPoolResults vkCmdCopyQueryPoolResults;
|
||||
PFN_vkCmdPushConstants vkCmdPushConstants;
|
||||
PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass;
|
||||
PFN_vkCmdNextSubpass vkCmdNextSubpass;
|
||||
PFN_vkCmdEndRenderPass vkCmdEndRenderPass;
|
||||
PFN_vkCmdExecuteCommands vkCmdExecuteCommands;
|
||||
|
||||
#ifdef __ANDROID__
|
||||
@@ -198,37 +204,142 @@ static HINSTANCE vulkanLibrary;
|
||||
static void *vulkanLibrary;
|
||||
#endif
|
||||
|
||||
bool g_vulkanAvailabilityChecked = false;
|
||||
bool g_vulkanMayBeAvailable = false;
|
||||
|
||||
#define LOAD_INSTANCE_FUNC(instance, x) x = (PFN_ ## x)vkGetInstanceProcAddr(instance, #x); if (!x) {ILOG("Missing (instance): %s", #x);}
|
||||
#define LOAD_DEVICE_FUNC(instance, x) x = (PFN_ ## x)vkGetDeviceProcAddr(instance, #x); if (!x) {ILOG("Missing (device): %s", #x);}
|
||||
#define LOAD_GLOBAL_FUNC(x) x = (PFN_ ## x)dlsym(vulkanLibrary, #x); if (!x) {ILOG("Missing (global): %s", #x);}
|
||||
|
||||
#define LOAD_GLOBAL_FUNC_LOCAL(lib, x) (PFN_ ## x)dlsym(lib, #x);
|
||||
|
||||
static const char *so_names[] = {
|
||||
"libvulkan.so",
|
||||
"libvulkan.so.1",
|
||||
};
|
||||
|
||||
void VulkanSetAvailable(bool available) {
|
||||
g_vulkanAvailabilityChecked = true;
|
||||
g_vulkanMayBeAvailable = available;
|
||||
}
|
||||
|
||||
bool VulkanMayBeAvailable() {
|
||||
if (vulkanLibrary)
|
||||
return true;
|
||||
bool available = false;
|
||||
if (g_vulkanAvailabilityChecked)
|
||||
return g_vulkanMayBeAvailable;
|
||||
#ifndef _WIN32
|
||||
void *lib = nullptr;
|
||||
for (int i = 0; i < ARRAY_SIZE(so_names); i++) {
|
||||
void *lib = dlopen(so_names[i], RTLD_NOW | RTLD_LOCAL);
|
||||
lib = dlopen(so_names[i], RTLD_NOW | RTLD_LOCAL);
|
||||
if (lib) {
|
||||
available = true;
|
||||
dlclose(lib);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
// LoadLibrary etc
|
||||
HINSTANCE lib = LoadLibrary(L"vulkan-1.dll");
|
||||
available = lib != 0;
|
||||
if (lib) {
|
||||
FreeLibrary(lib);
|
||||
}
|
||||
#endif
|
||||
return available;
|
||||
if (!lib) {
|
||||
g_vulkanAvailabilityChecked = true;
|
||||
g_vulkanMayBeAvailable = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do a hyper minimal initialization and teardown to figure out if there's any chance
|
||||
// that any sort of Vulkan will be usable.
|
||||
PFN_vkCreateInstance localCreateInstance = LOAD_GLOBAL_FUNC_LOCAL(lib, vkCreateInstance);
|
||||
PFN_vkEnumeratePhysicalDevices localEnumerate = LOAD_GLOBAL_FUNC_LOCAL(lib, vkEnumeratePhysicalDevices);
|
||||
PFN_vkDestroyInstance localDestroyInstance = LOAD_GLOBAL_FUNC_LOCAL(lib, vkDestroyInstance);
|
||||
PFN_vkGetPhysicalDeviceProperties localGetPhysicalDeviceProperties = LOAD_GLOBAL_FUNC_LOCAL(lib, vkGetPhysicalDeviceProperties);
|
||||
|
||||
// Need to predeclare all this because of the gotos...
|
||||
VkInstanceCreateInfo ci{ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
|
||||
VkApplicationInfo info{ VK_STRUCTURE_TYPE_APPLICATION_INFO };
|
||||
std::vector<VkPhysicalDevice> devices;
|
||||
bool anyGood = false;
|
||||
const char *instanceExtensions[1]{};
|
||||
VkInstance instance = VK_NULL_HANDLE;
|
||||
VkResult res;
|
||||
uint32_t physicalDeviceCount = 0;
|
||||
|
||||
if (!localCreateInstance || !localEnumerate || !localDestroyInstance || !localGetPhysicalDeviceProperties)
|
||||
goto bail;
|
||||
|
||||
#ifdef _WIN32
|
||||
instanceExtensions[0] = VK_KHR_WIN32_SURFACE_EXTENSION_NAME;
|
||||
ci.enabledExtensionCount = 1;
|
||||
#elif defined(__ANDROID__)
|
||||
instanceExtensions[0] = VK_KHR_ANDROID_SURFACE_EXTENSION_NAME;
|
||||
ci.enabledExtensionCount = 1;
|
||||
#endif
|
||||
|
||||
ci.ppEnabledExtensionNames = instanceExtensions;
|
||||
ci.enabledLayerCount = 0;
|
||||
info.apiVersion = VK_API_VERSION_1_0;
|
||||
info.applicationVersion = 1;
|
||||
info.engineVersion = 1;
|
||||
info.pApplicationName = "VulkanChecker";
|
||||
info.pEngineName = "VulkanChecker";
|
||||
ci.pApplicationInfo = &info;
|
||||
ci.flags = 0;
|
||||
res = localCreateInstance(&ci, nullptr, &instance);
|
||||
if (res != VK_SUCCESS) {
|
||||
instance = nullptr;
|
||||
ELOG("Failed to create vulkan instance.");
|
||||
goto bail;
|
||||
}
|
||||
ILOG("Vulkan test instance created successfully.");
|
||||
res = localEnumerate(instance, &physicalDeviceCount, nullptr);
|
||||
if (res != VK_SUCCESS) {
|
||||
ELOG("Failed to count physical devices.");
|
||||
goto bail;
|
||||
}
|
||||
if (physicalDeviceCount == 0) {
|
||||
ELOG("No physical Vulkan devices.");
|
||||
goto bail;
|
||||
}
|
||||
devices.resize(physicalDeviceCount);
|
||||
res = localEnumerate(instance, &physicalDeviceCount, devices.data());
|
||||
if (res != VK_SUCCESS) {
|
||||
ELOG("Failed to enumerate physical devices.");
|
||||
goto bail;
|
||||
}
|
||||
anyGood = false;
|
||||
for (auto device : devices) {
|
||||
VkPhysicalDeviceProperties props;
|
||||
localGetPhysicalDeviceProperties(device, &props);
|
||||
switch (props.deviceType) {
|
||||
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
|
||||
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
|
||||
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
|
||||
anyGood = true;
|
||||
break;
|
||||
}
|
||||
// TODO: Should also check queuefamilyproperties for a GRAPHICS queue family? Oh well.
|
||||
}
|
||||
|
||||
if (!anyGood) {
|
||||
WLOG("Found Vulkan API, but no good Vulkan device!");
|
||||
g_vulkanMayBeAvailable = false;
|
||||
} else {
|
||||
ILOG("Found working Vulkan API!");
|
||||
g_vulkanMayBeAvailable = true;
|
||||
}
|
||||
|
||||
bail:
|
||||
g_vulkanAvailabilityChecked = true;
|
||||
if (instance) {
|
||||
localDestroyInstance(instance, nullptr);
|
||||
}
|
||||
if (lib) {
|
||||
#ifndef _WIN32
|
||||
dlclose(lib);
|
||||
#else
|
||||
FreeLibrary(lib);
|
||||
#endif
|
||||
} else {
|
||||
ELOG("Vulkan with working device not detected.");
|
||||
}
|
||||
return g_vulkanMayBeAvailable;
|
||||
}
|
||||
|
||||
bool VulkanLoad() {
|
||||
|
||||
@@ -201,6 +201,7 @@ extern PFN_vkDestroyDebugReportCallbackEXT dyn_vkDestroyDebugReportCallbackEXT;
|
||||
|
||||
// Way to do a quick check before even attempting to load.
|
||||
bool VulkanMayBeAvailable();
|
||||
void VulkanSetAvailable(bool available);
|
||||
|
||||
bool VulkanLoad();
|
||||
void VulkanLoadInstanceFunctions(VkInstance instance);
|
||||
|
||||
@@ -36,7 +36,7 @@ VulkanPushBuffer::~VulkanPushBuffer() {
|
||||
bool VulkanPushBuffer::AddBuffer() {
|
||||
BufInfo info;
|
||||
|
||||
VkBufferCreateInfo b = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
|
||||
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 | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
||||
@@ -56,7 +56,7 @@ bool VulkanPushBuffer::AddBuffer() {
|
||||
// TODO: We really should use memoryTypeIndex here..
|
||||
|
||||
// Okay, that's the buffer. Now let's allocate some memory for it.
|
||||
VkMemoryAllocateInfo alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
||||
VkMemoryAllocateInfo alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
||||
// TODO: Should check here that memoryTypeIndex_ matches reqs.memoryTypeBits.
|
||||
alloc.memoryTypeIndex = memoryTypeIndex_;
|
||||
alloc.allocationSize = reqs.size;
|
||||
@@ -75,9 +75,8 @@ bool VulkanPushBuffer::AddBuffer() {
|
||||
return false;
|
||||
}
|
||||
|
||||
buf_ = buffers_.size();
|
||||
buffers_.resize(buf_ + 1);
|
||||
buffers_[buf_] = info;
|
||||
buffers_.push_back(info);
|
||||
buf_ = buffers_.size() - 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -136,17 +135,17 @@ size_t VulkanPushBuffer::GetTotalSize() const {
|
||||
}
|
||||
|
||||
void VulkanPushBuffer::Map() {
|
||||
assert(!writePtr_);
|
||||
_dbg_assert_(G3D, !writePtr_);
|
||||
VkResult res = vkMapMemory(device_, buffers_[buf_].deviceMemory, 0, size_, 0, (void **)(&writePtr_));
|
||||
assert(writePtr_);
|
||||
_dbg_assert_(G3D, writePtr_);
|
||||
assert(VK_SUCCESS == res);
|
||||
}
|
||||
|
||||
void VulkanPushBuffer::Unmap() {
|
||||
assert(writePtr_);
|
||||
_dbg_assert_(G3D, writePtr_ != 0);
|
||||
/*
|
||||
// Should not need this since we use coherent memory.
|
||||
VkMappedMemoryRange range = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
|
||||
VkMappedMemoryRange range{ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
|
||||
range.offset = 0;
|
||||
range.size = offset_;
|
||||
range.memory = buffers_[buf_].deviceMemory;
|
||||
@@ -284,9 +283,9 @@ bool VulkanDeviceAllocator::AllocateFromSlab(Slab &slab, size_t &start, size_t b
|
||||
int VulkanDeviceAllocator::ComputeUsagePercent() const {
|
||||
int blockSum = 0;
|
||||
int blocksUsed = 0;
|
||||
for (int i = 0; i < slabs_.size(); i++) {
|
||||
for (size_t i = 0; i < slabs_.size(); i++) {
|
||||
blockSum += (int)slabs_[i].usage.size();
|
||||
for (int j = 0; j < slabs_[i].usage.size(); j++) {
|
||||
for (size_t j = 0; j < slabs_[i].usage.size(); j++) {
|
||||
blocksUsed += slabs_[i].usage[j] != 0 ? 1 : 0;
|
||||
}
|
||||
}
|
||||
@@ -360,17 +359,14 @@ void VulkanDeviceAllocator::ExecuteFree(FreeInfo *userdata) {
|
||||
slab.allocSizes.erase(it);
|
||||
} else {
|
||||
// Ack, a double free?
|
||||
_assert_msg_(G3D, false, "Double free? Block missing at offset %d", userdata->offset);
|
||||
_assert_msg_(G3D, false, "Double free? Block missing at offset %d", (int)userdata->offset);
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Wrong deviceMemory even? Maybe it was already decimated, but that means a double-free.
|
||||
if (!found) {
|
||||
Crash();
|
||||
}
|
||||
|
||||
_assert_msg_(G3D, found, "ExecuteFree: Block not found (offset %d)", (int)offset);
|
||||
delete userdata;
|
||||
}
|
||||
|
||||
@@ -381,7 +377,7 @@ bool VulkanDeviceAllocator::AllocateSlab(VkDeviceSize minBytes) {
|
||||
minSlabSize_ <<= 1;
|
||||
}
|
||||
|
||||
VkMemoryAllocateInfo alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
||||
VkMemoryAllocateInfo alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
||||
alloc.allocationSize = minSlabSize_;
|
||||
alloc.memoryTypeIndex = memoryTypeIndex_;
|
||||
|
||||
|
||||
+2
-3
@@ -506,7 +506,6 @@ static ConfigSetting graphicsSettings[] = {
|
||||
ReportedConfigSetting("RenderingMode", &g_Config.iRenderingMode, &DefaultRenderingMode, true, true),
|
||||
ConfigSetting("SoftwareRenderer", &g_Config.bSoftwareRendering, false, true, true),
|
||||
ReportedConfigSetting("HardwareTransform", &g_Config.bHardwareTransform, true, true, true),
|
||||
ReportedConfigSetting("SoftwareSkinning", &g_Config.bSoftwareSkinning, true, true, true),
|
||||
ReportedConfigSetting("TextureFiltering", &g_Config.iTexFiltering, 1, true, true),
|
||||
ReportedConfigSetting("BufferFiltering", &g_Config.iBufFilter, 1, true, true),
|
||||
ReportedConfigSetting("InternalResolution", &g_Config.iInternalResolution, &DefaultInternalResolution, true, true),
|
||||
@@ -723,8 +722,8 @@ static ConfigSetting controlSettings[] = {
|
||||
ConfigSetting("UseMouse", &g_Config.bMouseControl, false, true, true),
|
||||
ConfigSetting("MapMouse", &g_Config.bMapMouse, false, true, true),
|
||||
ConfigSetting("ConfineMap", &g_Config.bMouseConfine, false, true, true),
|
||||
ConfigSetting("MouseSensitivity", &g_Config.fMouseSensitivity, 0.1, true, true),
|
||||
ConfigSetting("MouseSmoothing", &g_Config.fMouseSmoothing, 0.9, true, true),
|
||||
ConfigSetting("MouseSensitivity", &g_Config.fMouseSensitivity, 0.1f, true, true),
|
||||
ConfigSetting("MouseSmoothing", &g_Config.fMouseSmoothing, 0.9f, true, true),
|
||||
|
||||
ConfigSetting(false),
|
||||
};
|
||||
|
||||
@@ -160,7 +160,6 @@ public:
|
||||
int iGPUBackend;
|
||||
bool bSoftwareRendering;
|
||||
bool bHardwareTransform; // only used in the GLES backend
|
||||
bool bSoftwareSkinning; // may speed up some games
|
||||
|
||||
int iRenderingMode; // 0 = non-buffered rendering 1 = buffered rendering
|
||||
int iTexFiltering; // 1 = off , 2 = nearest , 3 = linear , 4 = linear(CG)
|
||||
|
||||
+5
-6
@@ -37,6 +37,7 @@
|
||||
#include "Common/GraphicsContext.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Common/CommonWindows.h"
|
||||
#include "Windows/InputDevice.h"
|
||||
#endif
|
||||
|
||||
@@ -176,7 +177,7 @@ bool UpdateScreenScale(int width, int height) {
|
||||
dp_yres = new_dp_yres;
|
||||
pixel_xres = width;
|
||||
pixel_yres = height;
|
||||
INFO_LOG(SYSTEM, "pixel_res: %dx%d. Calling NativeResized()", pixel_xres, pixel_yres);
|
||||
DEBUG_LOG(SYSTEM, "pixel_res: %dx%d. Calling NativeResized()", pixel_xres, pixel_yres);
|
||||
NativeResized();
|
||||
return true;
|
||||
}
|
||||
@@ -189,14 +190,11 @@ void UpdateRunLoop() {
|
||||
return;
|
||||
}
|
||||
NativeUpdate();
|
||||
|
||||
if (GetUIState() != UISTATE_EXIT) {
|
||||
NativeRender(graphicsContext);
|
||||
}
|
||||
NativeRender(graphicsContext);
|
||||
}
|
||||
|
||||
void KeepScreenAwake() {
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
|
||||
SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
|
||||
#endif
|
||||
}
|
||||
@@ -268,6 +266,7 @@ reswitch:
|
||||
if (GetUIState() != UISTATE_INGAME) {
|
||||
CoreStateProcessed();
|
||||
if (GetUIState() == UISTATE_EXIT) {
|
||||
UpdateRunLoop();
|
||||
return;
|
||||
}
|
||||
Core_RunLoop(ctx);
|
||||
|
||||
@@ -24,11 +24,15 @@ class GraphicsContext;
|
||||
|
||||
// called from emu thread
|
||||
void UpdateRunLoop();
|
||||
|
||||
void Core_Run(GraphicsContext *ctx);
|
||||
void Core_Stop();
|
||||
void Core_ErrorPause();
|
||||
// For platforms that don't call Core_Run
|
||||
void Core_SetGraphicsContext(GraphicsContext *ctx);
|
||||
|
||||
void Core_RunRenderThreadFrame();
|
||||
|
||||
// called from gui
|
||||
void Core_EnableStepping(bool step);
|
||||
void Core_DoSingleStep();
|
||||
|
||||
+4
-3
@@ -30,10 +30,11 @@ void hleCheat(u64 userdata, int cyclesLate);
|
||||
|
||||
static inline std::string TrimString(const std::string &s) {
|
||||
auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c) {
|
||||
return std::isspace(c);
|
||||
// isspace() expects 0 - 255, so convert any sign-extended value.
|
||||
return std::isspace(c & 0xFF);
|
||||
});
|
||||
auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c){
|
||||
return std::isspace(c);
|
||||
return std::isspace(c & 0xFF);
|
||||
}).base();
|
||||
return wsback > wsfront ? std::string(wsfront, wsback) : std::string();
|
||||
}
|
||||
@@ -351,7 +352,7 @@ std::vector<std::string> CWCheatEngine::GetCodesList() {
|
||||
// It also goes through other "_" lines, but they all have to meet this requirement anyway
|
||||
// so we don't have to specify any syntax checks here that are made by cheat engine.
|
||||
if (line.length() >= 5 && line[0] == '_') {
|
||||
for (int i = 4; i < line.length(); i++) {
|
||||
for (size_t i = 4; i < line.length(); i++) {
|
||||
if (line[i] != ' ') {
|
||||
validCheatLine = true;
|
||||
break;
|
||||
|
||||
@@ -395,8 +395,8 @@ void PSPSaveDialog::DisplaySaveDataInfo1()
|
||||
} else {
|
||||
char title[512];
|
||||
char time[512];
|
||||
char saveTitle[512];
|
||||
char saveDetail[512];
|
||||
char saveTitle[1024];
|
||||
char saveDetail[1024];
|
||||
|
||||
char am_pm[] = "AM";
|
||||
char hour_time[10] ;
|
||||
@@ -421,27 +421,27 @@ void PSPSaveDialog::DisplaySaveDataInfo1()
|
||||
snprintf(hour_time,10,"%02d:%02d %s", hour, min, am_pm);
|
||||
}
|
||||
|
||||
snprintf(title, 512, "%s", param.GetFileInfo(currentSelectedSave).title);
|
||||
snprintf(title, sizeof(title), "%s", param.GetFileInfo(currentSelectedSave).title);
|
||||
int day = param.GetFileInfo(currentSelectedSave).modif_time.tm_mday;
|
||||
int month = param.GetFileInfo(currentSelectedSave).modif_time.tm_mon + 1;
|
||||
int year = param.GetFileInfo(currentSelectedSave).modif_time.tm_year + 1900;
|
||||
s64 sizeK = param.GetFileInfo(currentSelectedSave).size / 1024;
|
||||
switch (g_Config.iDateFormat) {
|
||||
case 1:
|
||||
snprintf(time, 512, "%d/%02d/%02d %s %lld KB", year, month, day, hour_time, sizeK);
|
||||
snprintf(time, sizeof(time), "%d/%02d/%02d %s %lld KB", year, month, day, hour_time, sizeK);
|
||||
break;
|
||||
case 2:
|
||||
snprintf(time, 512, "%02d/%02d/%d %s %lld KB", month, day, year, hour_time, sizeK);
|
||||
snprintf(time, sizeof(time), "%02d/%02d/%d %s %lld KB", month, day, year, hour_time, sizeK);
|
||||
break;
|
||||
case 3:
|
||||
snprintf(time, 512, "%02d/%02d/%d %s %lld KB", day, month, year, hour_time, sizeK);
|
||||
snprintf(time, sizeof(time), "%02d/%02d/%d %s %lld KB", day, month, year, hour_time, sizeK);
|
||||
break;
|
||||
default:
|
||||
snprintf(time, 512, "%d/%02d/%02d %s %lld KB", year, month, day, hour_time, sizeK);
|
||||
snprintf(time, sizeof(time), "%d/%02d/%02d %s %lld KB", year, month, day, hour_time, sizeK);
|
||||
}
|
||||
snprintf(saveTitle, 512, "%s", param.GetFileInfo(currentSelectedSave).saveTitle);
|
||||
snprintf(saveDetail, 512, "%s", param.GetFileInfo(currentSelectedSave).saveDetail);
|
||||
|
||||
snprintf(saveTitle, sizeof(saveTitle), "%s", param.GetFileInfo(currentSelectedSave).saveTitle);
|
||||
snprintf(saveDetail, sizeof(saveDetail), "%s", param.GetFileInfo(currentSelectedSave).saveDetail);
|
||||
|
||||
PPGeDrawRect(180, 136, 480, 137, CalcFadedColor(0xFFFFFFFF));
|
||||
std::string titleTxt = title;
|
||||
std::string timeTxt = time;
|
||||
|
||||
@@ -29,7 +29,7 @@ const char *ElfReader::GetSectionName(int section) const {
|
||||
return nullptr;
|
||||
|
||||
int nameOffset = sections[section].sh_name;
|
||||
if (nameOffset < 0 || nameOffset >= size_) {
|
||||
if (nameOffset < 0 || (size_t)nameOffset >= size_) {
|
||||
ERROR_LOG(LOADER, "ELF: Bad name offset %d in section %d (max = %d)", nameOffset, section, (int)size_);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ PBPReader::PBPReader(FileLoader *fileLoader) : file_(nullptr), header_(), isELF_
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOADER, "Loading PBP, version = %08x", header_.version);
|
||||
VERBOSE_LOG(LOADER, "Loading PBP, version = %08x", header_.version);
|
||||
file_ = fileLoader;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2017- PPSSPP Project.
|
||||
// Copyright (c) 2017- 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
|
||||
@@ -50,9 +50,9 @@ void BlobFileSystem::CloseFile(u32 handle) {
|
||||
size_t BlobFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) {
|
||||
auto entry = entries_.find(handle);
|
||||
if (entry != entries_.end()) {
|
||||
size_t readSize = fileLoader_->ReadAt(entry->second, size, pointer);
|
||||
s64 readSize = (s64)fileLoader_->ReadAt(entry->second, (size_t)size, pointer);
|
||||
entry->second += readSize;
|
||||
return readSize;
|
||||
return (size_t)readSize;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ bool DirectoryFileHandle::Open(std::string &basePath, std::string &fileName, Fil
|
||||
#endif
|
||||
|
||||
std::string fullName = GetLocalPath(basePath,fileName);
|
||||
DEBUG_LOG(FILESYS,"Actually opening %s", fullName.c_str());
|
||||
VERBOSE_LOG(FILESYS,"Actually opening %s", fullName.c_str());
|
||||
|
||||
// On the PSP, truncating doesn't lose data. If you seek later, you'll recover it.
|
||||
// This is abnormal, so we deviate from the PSP's behavior and truncate on write/close.
|
||||
@@ -1002,7 +1002,7 @@ u32 VFSFileSystem::OpenFile(std::string filename, FileAccess access, const char
|
||||
|
||||
std::string fullName = GetLocalPath(filename);
|
||||
const char *fullNameC = fullName.c_str();
|
||||
DEBUG_LOG(FILESYS,"VFSFileSystem actually opening %s (%s)", fullNameC, filename.c_str());
|
||||
VERBOSE_LOG(FILESYS,"VFSFileSystem actually opening %s (%s)", fullNameC, filename.c_str());
|
||||
|
||||
size_t size;
|
||||
u8 *data = VFSReadFile(fullNameC, &size);
|
||||
|
||||
@@ -863,7 +863,9 @@ void __DisplaySetFramebuf(u32 topaddr, int linesize, int pixelFormat, int sync)
|
||||
framebuf = fbstate;
|
||||
gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.stride, framebuf.fmt);
|
||||
// IMMEDIATE means that the buffer is fine. We can just flip immediately.
|
||||
if (!flippedThisFrame)
|
||||
// Doing it in non-buffered though creates problems (black screen) on occasion though
|
||||
// so let's not.
|
||||
if (!flippedThisFrame && g_Config.iRenderingMode != FB_NON_BUFFERED_MODE)
|
||||
__DisplayFlip(0);
|
||||
} else {
|
||||
// Delay the write until vblank
|
||||
|
||||
@@ -1268,7 +1268,8 @@ static Module *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 loadAdd
|
||||
scan = g_Config.bFuncReplacements;
|
||||
#endif
|
||||
|
||||
bool gotSymbols = scan && reader.LoadSymbols();
|
||||
// If the ELF has debug symbols, don't add entries to the symbol table.
|
||||
bool insertSymbols = scan && !reader.LoadSymbols();
|
||||
std::vector<SectionID> codeSections = reader.GetCodeSections();
|
||||
for (SectionID id : codeSections) {
|
||||
u32 start = reader.GetSectionAddr(id);
|
||||
@@ -1280,8 +1281,9 @@ static Module *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 loadAdd
|
||||
if (end > module->textEnd)
|
||||
module->textEnd = end;
|
||||
|
||||
if (scan)
|
||||
MIPSAnalyst::ScanForFunctions(start, end, !gotSymbols);
|
||||
if (scan) {
|
||||
insertSymbols = MIPSAnalyst::ScanForFunctions(start, end, insertSymbols);
|
||||
}
|
||||
}
|
||||
|
||||
// Some games don't have any sections at all.
|
||||
@@ -1290,14 +1292,18 @@ static Module *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 loadAdd
|
||||
u32 scanEnd = module->textEnd;
|
||||
// Skip the exports and imports sections, they're not code.
|
||||
if (scanEnd >= std::min(modinfo->libent, modinfo->libstub)) {
|
||||
MIPSAnalyst::ScanForFunctions(scanStart, std::min(modinfo->libent, modinfo->libstub) - 4, !gotSymbols);
|
||||
insertSymbols = MIPSAnalyst::ScanForFunctions(scanStart, std::min(modinfo->libent, modinfo->libstub) - 4, insertSymbols);
|
||||
scanStart = std::min(modinfo->libentend, modinfo->libstubend);
|
||||
}
|
||||
if (scanEnd >= std::max(modinfo->libent, modinfo->libstub)) {
|
||||
MIPSAnalyst::ScanForFunctions(scanStart, std::max(modinfo->libent, modinfo->libstub) - 4, !gotSymbols);
|
||||
insertSymbols = MIPSAnalyst::ScanForFunctions(scanStart, std::max(modinfo->libent, modinfo->libstub) - 4, insertSymbols);
|
||||
scanStart = std::max(modinfo->libentend, modinfo->libstubend);
|
||||
}
|
||||
MIPSAnalyst::ScanForFunctions(scanStart, scanEnd, !gotSymbols);
|
||||
insertSymbols = MIPSAnalyst::ScanForFunctions(scanStart, scanEnd, insertSymbols);
|
||||
}
|
||||
|
||||
if (scan) {
|
||||
MIPSAnalyst::FinalizeScan(insertSymbols);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1577,6 +1583,7 @@ void __KernelLoadReset() {
|
||||
HLEShutdown();
|
||||
Replacement_Init();
|
||||
HLEInit();
|
||||
assert(gpu);
|
||||
gpu->Reinitialize();
|
||||
}
|
||||
|
||||
@@ -1587,6 +1594,8 @@ void __KernelLoadReset() {
|
||||
bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_string) {
|
||||
SceKernelLoadExecParam param;
|
||||
|
||||
PSP_SetLoading("Loading game...");
|
||||
|
||||
if (paramPtr)
|
||||
Memory::ReadStruct(paramPtr, ¶m);
|
||||
else
|
||||
@@ -1625,6 +1634,7 @@ bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_str
|
||||
|
||||
pspFileSystem.ReadFile(handle, temp, (size_t)info.size);
|
||||
|
||||
PSP_SetLoading("Loading modules...");
|
||||
Module *module = __KernelLoadModule(temp, (size_t)info.size, 0, error_string);
|
||||
|
||||
if (!module || module->isFake) {
|
||||
@@ -1665,6 +1675,7 @@ bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_str
|
||||
if (module->nm.module_start_thread_stacksize != 0)
|
||||
option.stacksize = module->nm.module_start_thread_stacksize;
|
||||
|
||||
PSP_SetLoading("Starting modules...");
|
||||
if (paramPtr)
|
||||
__KernelStartModule(module, param.args, (const char*)param_argp, &option);
|
||||
else
|
||||
@@ -1681,6 +1692,7 @@ bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_str
|
||||
|
||||
bool __KernelLoadGEDump(const std::string &base_filename, std::string *error_string) {
|
||||
__KernelLoadReset();
|
||||
PSP_SetLoading("Generating code...");
|
||||
|
||||
mipsr4k.pc = PSP_GetUserMemoryBase();
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ StereoResampler::StereoResampler()
|
||||
|
||||
StereoResampler::~StereoResampler() {
|
||||
delete[] m_buffer;
|
||||
m_buffer = nullptr;
|
||||
}
|
||||
|
||||
void StereoResampler::UpdateBufferSize() {
|
||||
|
||||
@@ -179,20 +179,10 @@ void ArmJit::CompileDelaySlot(int flags)
|
||||
_MSR(true, false, R8); // Restore flags register
|
||||
}
|
||||
|
||||
|
||||
void ArmJit::Compile(u32 em_address) {
|
||||
PROFILE_THIS_SCOPE("jitc");
|
||||
|
||||
#if PPSSPP_PLATFORM(UWP)
|
||||
// INFO_LOG(JIT, "Compiling at %08x", em_address);
|
||||
// Unfortunately Microsoft forgot to expose FlushInstructionCache to UWP applications... even though they expose
|
||||
// the ability to generate code :( This works great on x86 but on ARM we're out of luck.
|
||||
// This seems to be enough to flush the instruction cache:
|
||||
// If this OutputDebugStringUTF8 is not called, we crash.
|
||||
// But it only helps if the debugger is attached :( If not, we still crash.
|
||||
// So really, the JIT is broken on Windows UWP ARM.
|
||||
OutputDebugStringUTF8("JITHACK");
|
||||
#endif
|
||||
|
||||
if (GetSpaceLeft() < 0x10000 || blocks.IsFull()) {
|
||||
ClearCache();
|
||||
|
||||
@@ -878,13 +878,16 @@ namespace MIPSAnalyst {
|
||||
|
||||
for (auto iter = functions.begin(), end = functions.end(); iter != end; iter++) {
|
||||
AnalyzedFunction &f = *iter;
|
||||
if (Memory::IsValidRange(f.start, f.end - f.start + 4)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// This is unfortunate. In case of emuhacks or relocs, we have to make a copy.
|
||||
buffer.resize((f.end - f.start + 4) / 4);
|
||||
size_t pos = 0;
|
||||
for (u32 addr = f.start; addr <= f.end; addr += 4) {
|
||||
u32 validbits = 0xFFFFFFFF;
|
||||
MIPSOpcode instr = Memory::Read_Instruction(addr, true);
|
||||
MIPSOpcode instr = Memory::ReadUnchecked_Instruction(addr, true);
|
||||
if (MIPS_IS_EMUHACK(instr)) {
|
||||
f.hasHash = false;
|
||||
goto skip;
|
||||
@@ -1015,7 +1018,7 @@ skip:
|
||||
return furthestJumpbackAddr;
|
||||
}
|
||||
|
||||
void ScanForFunctions(u32 startAddr, u32 endAddr, bool insertSymbols) {
|
||||
bool ScanForFunctions(u32 startAddr, u32 endAddr, bool insertSymbols) {
|
||||
std::lock_guard<std::recursive_mutex> guard(functions_lock);
|
||||
|
||||
AnalyzedFunction currentFunction = {startAddr};
|
||||
@@ -1164,6 +1167,10 @@ skip:
|
||||
}
|
||||
}
|
||||
|
||||
return insertSymbols;
|
||||
}
|
||||
|
||||
void FinalizeScan(bool insertSymbols) {
|
||||
HashFunctions();
|
||||
|
||||
std::string hashMapFilename = GetSysDirectory(DIRECTORY_SYSTEM) + "knownfuncs.ini";
|
||||
|
||||
@@ -105,7 +105,9 @@ namespace MIPSAnalyst
|
||||
// If we have loaded symbols from the elf, we'll register functions as they are touched
|
||||
// so that we don't just dump them all in the cache.
|
||||
void RegisterFunction(u32 startAddr, u32 size, const char *name);
|
||||
void ScanForFunctions(u32 startAddr, u32 endAddr, bool insertSymbols);
|
||||
// Returns new insertSymbols value for FinalizeScan().
|
||||
bool ScanForFunctions(u32 startAddr, u32 endAddr, bool insertSymbols);
|
||||
void FinalizeScan(bool insertSymbols);
|
||||
void ForgetFunctions(u32 startAddr, u32 endAddr);
|
||||
void PrecompileFunctions();
|
||||
void PrecompileFunction(u32 startAddr, u32 length);
|
||||
|
||||
@@ -1360,8 +1360,8 @@ namespace MIPSInt
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
d[i] = 0.0f;
|
||||
for (int k = 0; k < n; k++)
|
||||
d[i] = s[i*4] * t[0];
|
||||
for (int k = 1; k < n; k++)
|
||||
{
|
||||
d[i] += s[i*4+k] * t[k];
|
||||
}
|
||||
@@ -1371,7 +1371,8 @@ namespace MIPSInt
|
||||
{
|
||||
Reporting::ReportMessage("Trying to interpret instruction that can't be interpreted (BADVTFM)");
|
||||
_dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted (BADVTFM)");
|
||||
memset(d, 0, sizeof(d));
|
||||
for (int i = 0; i < n; i++)
|
||||
d[i] = 0.0;
|
||||
}
|
||||
WriteVector(d, sz, vd);
|
||||
PC += 4;
|
||||
|
||||
+2
-2
@@ -83,8 +83,8 @@ extern u32 g_MemorySize;
|
||||
extern u32 g_PSPModel;
|
||||
|
||||
// UWP has such limited memory management that we need to mask
|
||||
// even in 64-bit mode.
|
||||
#if PPSSPP_ARCH(32BIT) || PPSSPP_PLATFORM(UWP)
|
||||
// even in 64-bit mode. Also, when using the sanitizer, we need to mask as well.
|
||||
#if PPSSPP_ARCH(32BIT) || PPSSPP_PLATFORM(UWP) || USE_ADDRESS_SANITIZER
|
||||
#define MASKED_PSP_MEMORY
|
||||
#endif
|
||||
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "file/file_util.h"
|
||||
#include "util/text/utf8.h"
|
||||
#include "thread/threadutil.h"
|
||||
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/StringUtils.h"
|
||||
@@ -253,6 +255,8 @@ bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) {
|
||||
INFO_LOG(LOADER,"Loading %s...", bootpath.c_str());
|
||||
|
||||
std::thread th([bootpath] {
|
||||
setCurrentThreadName("ExecLoader");
|
||||
PSP_SetLoading("Loading executable...");
|
||||
// TODO: We can't use the initial error_string pointer.
|
||||
bool success = __KernelLoadExec(bootpath.c_str(), 0, &PSP_CoreParameter().errorString);
|
||||
if (success && coreState == CORE_POWERUP) {
|
||||
|
||||
+18
-168
@@ -81,12 +81,9 @@ ParamSFOData g_paramSFO;
|
||||
static GlobalUIState globalUIState;
|
||||
static CoreParameter coreParameter;
|
||||
static FileLoader *loadedFile;
|
||||
static std::thread *cpuThread = nullptr;
|
||||
static std::thread::id cpuThreadID;
|
||||
static std::mutex cpuThreadLock;
|
||||
static std::condition_variable cpuThreadCond;
|
||||
static std::condition_variable cpuThreadReplyCond;
|
||||
static u64 cpuThreadUntil;
|
||||
static std::mutex loadingReasonLock;
|
||||
static std::string loadingReason;
|
||||
|
||||
bool audioInitialized;
|
||||
|
||||
bool coreCollectDebugStats = false;
|
||||
@@ -142,45 +139,6 @@ void Audio_Shutdown() {
|
||||
}
|
||||
}
|
||||
|
||||
bool IsOnSeparateCPUThread() {
|
||||
if (cpuThread != nullptr) {
|
||||
return cpuThreadID == std::this_thread::get_id();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CPU_SetStateNoLock(CPUThreadState to) {
|
||||
cpuThreadState = to;
|
||||
cpuThreadCond.notify_one();
|
||||
cpuThreadReplyCond.notify_one();
|
||||
}
|
||||
|
||||
void CPU_SetState(CPUThreadState to) {
|
||||
std::lock_guard<std::mutex> guard(cpuThreadLock);
|
||||
CPU_SetStateNoLock(to);
|
||||
}
|
||||
|
||||
bool CPU_NextState(CPUThreadState from, CPUThreadState to) {
|
||||
std::lock_guard<std::mutex> guard(cpuThreadLock);
|
||||
if (cpuThreadState == from) {
|
||||
CPU_SetStateNoLock(to);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CPU_NextStateNot(CPUThreadState from, CPUThreadState to) {
|
||||
std::lock_guard<std::mutex> guard(cpuThreadLock);
|
||||
if (cpuThreadState != from) {
|
||||
CPU_SetStateNoLock(to);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CPU_IsReady() {
|
||||
if (coreState == CORE_POWERUP)
|
||||
return false;
|
||||
@@ -195,13 +153,6 @@ bool CPU_HasPendingAction() {
|
||||
return cpuThreadState != CPU_THREAD_RUNNING;
|
||||
}
|
||||
|
||||
void CPU_WaitStatus(std::condition_variable &cond, bool (*pred)()) {
|
||||
std::unique_lock<std::mutex> guard(cpuThreadLock);
|
||||
while (!pred()) {
|
||||
cond.wait(guard);
|
||||
}
|
||||
}
|
||||
|
||||
void CPU_Shutdown();
|
||||
|
||||
void CPU_Init() {
|
||||
@@ -278,7 +229,6 @@ void CPU_Init() {
|
||||
if (!LoadFile(&loadedFile, &coreParameter.errorString)) {
|
||||
CPU_Shutdown();
|
||||
coreParameter.fileToStart = "";
|
||||
CPU_SetState(CPU_THREAD_NOT_RUNNING);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -321,52 +271,6 @@ void UpdateLoadedFile(FileLoader *fileLoader) {
|
||||
loadedFile = fileLoader;
|
||||
}
|
||||
|
||||
void CPU_RunLoop() {
|
||||
setCurrentThreadName("CPU");
|
||||
|
||||
if (CPU_NextState(CPU_THREAD_PENDING, CPU_THREAD_STARTING)) {
|
||||
CPU_Init();
|
||||
CPU_NextState(CPU_THREAD_STARTING, CPU_THREAD_RUNNING);
|
||||
} else if (!CPU_NextState(CPU_THREAD_RESUME, CPU_THREAD_RUNNING)) {
|
||||
ERROR_LOG(CPU, "CPU thread in unexpected state: %d", cpuThreadState);
|
||||
return;
|
||||
}
|
||||
|
||||
while (cpuThreadState != CPU_THREAD_SHUTDOWN)
|
||||
{
|
||||
CPU_WaitStatus(cpuThreadCond, &CPU_HasPendingAction);
|
||||
switch (cpuThreadState) {
|
||||
case CPU_THREAD_EXECUTE:
|
||||
mipsr4k.RunLoopUntil(cpuThreadUntil);
|
||||
CPU_NextState(CPU_THREAD_EXECUTE, CPU_THREAD_RUNNING);
|
||||
break;
|
||||
|
||||
// These are fine, just keep looping.
|
||||
case CPU_THREAD_RUNNING:
|
||||
case CPU_THREAD_SHUTDOWN:
|
||||
break;
|
||||
|
||||
case CPU_THREAD_QUIT:
|
||||
// Just leave the thread, CPU is switching off thread.
|
||||
CPU_SetState(CPU_THREAD_NOT_RUNNING);
|
||||
return;
|
||||
|
||||
default:
|
||||
ERROR_LOG(CPU, "CPU thread in unexpected state: %d", cpuThreadState);
|
||||
// Begin shutdown, otherwise we'd just spin on this bad state.
|
||||
CPU_SetState(CPU_THREAD_SHUTDOWN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (coreState != CORE_ERROR) {
|
||||
coreState = CORE_POWERDOWN;
|
||||
}
|
||||
|
||||
CPU_Shutdown();
|
||||
CPU_SetState(CPU_THREAD_NOT_RUNNING);
|
||||
}
|
||||
|
||||
void Core_UpdateState(CoreState newState) {
|
||||
if ((coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME) && newState != CORE_RUNNING)
|
||||
coreStatePending = true;
|
||||
@@ -384,11 +288,6 @@ void Core_UpdateDebugStats(bool collectStats) {
|
||||
gpuStats.ResetFrame();
|
||||
}
|
||||
|
||||
void System_Wake() {
|
||||
// Ping the threads so they check coreState.
|
||||
CPU_NextStateNot(CPU_THREAD_NOT_RUNNING, CPU_THREAD_SHUTDOWN);
|
||||
}
|
||||
|
||||
// Ugly!
|
||||
static bool pspIsInited = false;
|
||||
static bool pspIsIniting = false;
|
||||
@@ -414,18 +313,9 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) {
|
||||
}
|
||||
coreParameter.errorString = "";
|
||||
pspIsIniting = true;
|
||||
PSP_SetLoading("Loading game...");
|
||||
|
||||
// Keeping this around because we might need it in the future.
|
||||
const bool separateCPUThread = false;
|
||||
if (separateCPUThread) {
|
||||
Core_ListenShutdown(System_Wake);
|
||||
CPU_SetState(CPU_THREAD_PENDING);
|
||||
cpuThread = new std::thread(&CPU_RunLoop);
|
||||
cpuThreadID = cpuThread->get_id();
|
||||
cpuThread->detach();
|
||||
} else {
|
||||
CPU_Init();
|
||||
}
|
||||
CPU_Init();
|
||||
|
||||
*error_string = coreParameter.errorString;
|
||||
bool success = coreParameter.fileToStart != "";
|
||||
@@ -447,6 +337,7 @@ bool PSP_InitUpdate(std::string *error_string) {
|
||||
bool success = coreParameter.fileToStart != "";
|
||||
*error_string = coreParameter.errorString;
|
||||
if (success && gpu == nullptr) {
|
||||
PSP_SetLoading("Starting graphics...");
|
||||
success = GPU_Init(coreParameter.graphicsContext, coreParameter.thin3d);
|
||||
if (!success) {
|
||||
PSP_Shutdown();
|
||||
@@ -461,11 +352,6 @@ bool PSP_InitUpdate(std::string *error_string) {
|
||||
bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) {
|
||||
PSP_InitStart(coreParam, error_string);
|
||||
|
||||
// For a potential resurrection of separate CPU thread later.
|
||||
if (false) {
|
||||
CPU_WaitStatus(cpuThreadReplyCond, &CPU_IsReady);
|
||||
}
|
||||
|
||||
while (!PSP_InitUpdate(error_string))
|
||||
sleep_ms(10);
|
||||
return pspIsInited;
|
||||
@@ -496,15 +382,7 @@ void PSP_Shutdown() {
|
||||
if (coreState == CORE_RUNNING)
|
||||
Core_UpdateState(CORE_ERROR);
|
||||
Core_NotifyShutdown();
|
||||
if (cpuThread != nullptr) {
|
||||
CPU_NextStateNot(CPU_THREAD_NOT_RUNNING, CPU_THREAD_SHUTDOWN);
|
||||
CPU_WaitStatus(cpuThreadReplyCond, &CPU_IsShutdown);
|
||||
delete cpuThread;
|
||||
cpuThread = 0;
|
||||
cpuThreadID = std::thread::id();
|
||||
} else {
|
||||
CPU_Shutdown();
|
||||
}
|
||||
CPU_Shutdown();
|
||||
GPU_Shutdown();
|
||||
g_paramSFO.Clear();
|
||||
host->SetWindowTitle(0);
|
||||
@@ -534,45 +412,7 @@ void PSP_RunLoopUntil(u64 globalticks) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We no longer allow a separate CPU thread but if we add a render queue
|
||||
// to GL we're gonna need it.
|
||||
bool useCPUThread = false;
|
||||
if (useCPUThread && cpuThread == nullptr) {
|
||||
// Need to start the cpu thread.
|
||||
Core_ListenShutdown(System_Wake);
|
||||
CPU_SetState(CPU_THREAD_RESUME);
|
||||
cpuThread = new std::thread(&CPU_RunLoop);
|
||||
cpuThreadID = cpuThread->get_id();
|
||||
cpuThread->detach();
|
||||
// Probably needs to tell the gpu that it will need to queue up its output
|
||||
// on another thread.
|
||||
CPU_WaitStatus(cpuThreadReplyCond, &CPU_IsReady);
|
||||
} else if (!useCPUThread && cpuThread != nullptr) {
|
||||
CPU_SetState(CPU_THREAD_QUIT);
|
||||
CPU_WaitStatus(cpuThreadReplyCond, &CPU_IsShutdown);
|
||||
delete cpuThread;
|
||||
cpuThread = nullptr;
|
||||
cpuThreadID = std::thread::id();
|
||||
}
|
||||
|
||||
if (cpuThread != nullptr) {
|
||||
cpuThreadUntil = globalticks;
|
||||
if (CPU_NextState(CPU_THREAD_RUNNING, CPU_THREAD_EXECUTE)) {
|
||||
// The CPU doesn't actually respect cpuThreadUntil well, especially when skipping frames.
|
||||
// TODO: Something smarter? Or force CPU to bail periodically?
|
||||
while (!CPU_IsReady()) {
|
||||
// Have the GPU do stuff here.
|
||||
if (coreState != CORE_RUNNING) {
|
||||
CPU_WaitStatus(cpuThreadReplyCond, &CPU_IsReady);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ERROR_LOG(CPU, "Unable to execute CPU run loop, unexpected state: %d", cpuThreadState);
|
||||
}
|
||||
} else {
|
||||
mipsr4k.RunLoopUntil(globalticks);
|
||||
}
|
||||
|
||||
mipsr4k.RunLoopUntil(globalticks);
|
||||
gpu->CleanupBeforeUI();
|
||||
}
|
||||
|
||||
@@ -580,6 +420,16 @@ void PSP_RunLoopFor(int cycles) {
|
||||
PSP_RunLoopUntil(CoreTiming::GetTicks() + cycles);
|
||||
}
|
||||
|
||||
void PSP_SetLoading(const std::string &reason) {
|
||||
std::lock_guard<std::mutex> guard(loadingReasonLock);
|
||||
loadingReason = reason;
|
||||
}
|
||||
|
||||
std::string PSP_GetLoading() {
|
||||
std::lock_guard<std::mutex> guard(loadingReasonLock);
|
||||
return loadingReason;
|
||||
}
|
||||
|
||||
CoreParameter &PSP_CoreParameter() {
|
||||
return coreParameter;
|
||||
}
|
||||
|
||||
+3
-2
@@ -73,13 +73,14 @@ void PSP_EndHostFrame();
|
||||
void PSP_RunLoopUntil(u64 globalticks);
|
||||
void PSP_RunLoopFor(int cycles);
|
||||
|
||||
void PSP_SetLoading(const std::string &reason);
|
||||
std::string PSP_GetLoading();
|
||||
|
||||
// Call before PSP_BeginHostFrame() in order to not miss any GPU stats.
|
||||
void Core_UpdateDebugStats(bool collectStats);
|
||||
|
||||
void Audio_Init();
|
||||
void Audio_Shutdown();
|
||||
|
||||
bool IsOnSeparateCPUThread();
|
||||
bool IsAudioInitialised();
|
||||
|
||||
void UpdateLoadedFile(FileLoader *fileLoader);
|
||||
|
||||
@@ -50,18 +50,6 @@ DrawEngineCommon::~DrawEngineCommon() {
|
||||
});
|
||||
}
|
||||
|
||||
void DrawEngineCommon::SetupVertexDecoder(u32 vertType) {
|
||||
// As the decoder depends on the UVGenMode when we use UV prescale, we simply mash it
|
||||
// into the top of the verttype where there are unused bits.
|
||||
const u32 vertTypeID = (vertType & 0xFFFFFF) | (gstate.getUVGenMode() << 24);
|
||||
|
||||
// If vtype has changed, setup the vertex decoder.
|
||||
if (vertTypeID != lastVType_) {
|
||||
dec_ = GetVertexDecoder(vertTypeID);
|
||||
lastVType_ = vertTypeID;
|
||||
}
|
||||
}
|
||||
|
||||
VertexDecoder *DrawEngineCommon::GetVertexDecoder(u32 vtype) {
|
||||
VertexDecoder *dec = decoderMap_.Get(vtype);
|
||||
if (dec)
|
||||
@@ -105,7 +93,7 @@ int DrawEngineCommon::ComputeNumVertsToDecode() const {
|
||||
void DrawEngineCommon::DecodeVerts(u8 *dest) {
|
||||
const UVScale origUV = gstate_c.uv;
|
||||
for (; decodeCounter_ < numDrawCalls; decodeCounter_++) {
|
||||
gstate_c.uv = uvScale[decodeCounter_];
|
||||
gstate_c.uv = drawCalls[decodeCounter_].uvScale;
|
||||
DecodeVertsStep(dest, decodeCounter_, decodedVerts_); // NOTE! DecodeVertsStep can modify decodeCounter_!
|
||||
}
|
||||
gstate_c.uv = origUV;
|
||||
@@ -330,11 +318,12 @@ bool DrawEngineCommon::GetCurrentSimpleVertices(int count, std::vector<GPUDebugV
|
||||
Matrix4ByMatrix4(worldviewproj, worldview, gstate.projMatrix);
|
||||
|
||||
vertices.resize(indexUpperBound + 1);
|
||||
uint32_t vertType = gstate.vertType;
|
||||
for (int i = indexLowerBound; i <= indexUpperBound; ++i) {
|
||||
const SimpleVertex &vert = simpleVertices[i];
|
||||
|
||||
if (gstate.isModeThrough()) {
|
||||
if (gstate.vertType & GE_VTYPE_TC_MASK) {
|
||||
if ((vertType & GE_VTYPE_THROUGH) != 0) {
|
||||
if (vertType & GE_VTYPE_TC_MASK) {
|
||||
vertices[i].u = vert.uv[0];
|
||||
vertices[i].v = vert.uv[1];
|
||||
} else {
|
||||
@@ -344,7 +333,7 @@ bool DrawEngineCommon::GetCurrentSimpleVertices(int count, std::vector<GPUDebugV
|
||||
vertices[i].x = vert.pos.x;
|
||||
vertices[i].y = vert.pos.y;
|
||||
vertices[i].z = vert.pos.z;
|
||||
if (gstate.vertType & GE_VTYPE_COL_MASK) {
|
||||
if (vertType & GE_VTYPE_COL_MASK) {
|
||||
memcpy(vertices[i].c, vert.color, sizeof(vertices[i].c));
|
||||
} else {
|
||||
memset(vertices[i].c, 0, sizeof(vertices[i].c));
|
||||
@@ -358,7 +347,7 @@ bool DrawEngineCommon::GetCurrentSimpleVertices(int count, std::vector<GPUDebugV
|
||||
Vec3f screenPos = ClipToScreen(clipPos);
|
||||
Vec3f drawPos = ScreenToDrawing(screenPos);
|
||||
|
||||
if (gstate.vertType & GE_VTYPE_TC_MASK) {
|
||||
if (vertType & GE_VTYPE_TC_MASK) {
|
||||
vertices[i].u = vert.uv[0] * (float)gstate.getTextureWidth(0);
|
||||
vertices[i].v = vert.uv[1] * (float)gstate.getTextureHeight(0);
|
||||
} else {
|
||||
@@ -369,7 +358,7 @@ bool DrawEngineCommon::GetCurrentSimpleVertices(int count, std::vector<GPUDebugV
|
||||
vertices[i].x = drawPos.x;
|
||||
vertices[i].y = drawPos.y;
|
||||
vertices[i].z = drawPos.z;
|
||||
if (gstate.vertType & GE_VTYPE_COL_MASK) {
|
||||
if (vertType & GE_VTYPE_COL_MASK) {
|
||||
memcpy(vertices[i].c, vert.color, sizeof(vertices[i].c));
|
||||
} else {
|
||||
memset(vertices[i].c, 0, sizeof(vertices[i].c));
|
||||
@@ -390,13 +379,9 @@ bool DrawEngineCommon::GetCurrentSimpleVertices(int count, std::vector<GPUDebugV
|
||||
// The implementation is initially a bit inefficient but shouldn't be a big deal.
|
||||
// An intermediate buffer of not-easy-to-predict size is stored at bufPtr.
|
||||
u32 DrawEngineCommon::NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr, VertexDecoder *dec, int lowerBound, int upperBound, u32 vertType) {
|
||||
// First, decode the vertices into a GPU compatible format. This step can be eliminated but will need a separate
|
||||
// implementation of the vertex decoder.
|
||||
// First, decode the vertices into a GPU compatible format.
|
||||
dec->DecodeVerts(bufPtr, inPtr, lowerBound, upperBound);
|
||||
|
||||
// OK, morphing eliminated but bones still remain to be taken care of.
|
||||
// Let's do a partial software transform where we only do skinning.
|
||||
|
||||
VertexReader reader(bufPtr, dec->GetDecVtxFmt(), vertType);
|
||||
|
||||
SimpleVertex *sverts = (SimpleVertex *)outPtr;
|
||||
@@ -408,56 +393,7 @@ u32 DrawEngineCommon::NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr,
|
||||
(u8)gstate.getMaterialAmbientA(),
|
||||
};
|
||||
|
||||
// Let's have two separate loops, one for non skinning and one for skinning.
|
||||
if (!g_Config.bSoftwareSkinning && (vertType & GE_VTYPE_WEIGHT_MASK) != GE_VTYPE_WEIGHT_NONE) {
|
||||
int numBoneWeights = vertTypeGetNumBoneWeights(vertType);
|
||||
for (int i = lowerBound; i <= upperBound; i++) {
|
||||
reader.Goto(i - lowerBound);
|
||||
SimpleVertex &sv = sverts[i];
|
||||
if (vertType & GE_VTYPE_TC_MASK) {
|
||||
reader.ReadUV(sv.uv);
|
||||
}
|
||||
|
||||
if (vertType & GE_VTYPE_COL_MASK) {
|
||||
reader.ReadColor0_8888(sv.color);
|
||||
} else {
|
||||
memcpy(sv.color, defaultColor, 4);
|
||||
}
|
||||
|
||||
float nrm[3], pos[3];
|
||||
float bnrm[3], bpos[3];
|
||||
|
||||
if (vertType & GE_VTYPE_NRM_MASK) {
|
||||
// Normals are generated during tessellation anyway, not sure if any need to supply
|
||||
reader.ReadNrm(nrm);
|
||||
} else {
|
||||
nrm[0] = 0;
|
||||
nrm[1] = 0;
|
||||
nrm[2] = 1.0f;
|
||||
}
|
||||
reader.ReadPos(pos);
|
||||
|
||||
// Apply skinning transform directly
|
||||
float weights[8];
|
||||
reader.ReadWeights(weights);
|
||||
// Skinning
|
||||
Vec3Packedf psum(0, 0, 0);
|
||||
Vec3Packedf nsum(0, 0, 0);
|
||||
for (int w = 0; w < numBoneWeights; w++) {
|
||||
if (weights[w] != 0.0f) {
|
||||
Vec3ByMatrix43(bpos, pos, gstate.boneMatrix + w * 12);
|
||||
Vec3Packedf tpos(bpos);
|
||||
psum += tpos * weights[w];
|
||||
|
||||
Norm3ByMatrix43(bnrm, nrm, gstate.boneMatrix + w * 12);
|
||||
Vec3Packedf tnorm(bnrm);
|
||||
nsum += tnorm * weights[w];
|
||||
}
|
||||
}
|
||||
sv.pos = psum;
|
||||
sv.nrm = nsum;
|
||||
}
|
||||
} else {
|
||||
{
|
||||
for (int i = lowerBound; i <= upperBound; i++) {
|
||||
reader.Goto(i - lowerBound);
|
||||
SimpleVertex &sv = sverts[i];
|
||||
@@ -665,6 +601,70 @@ ReliableHashType DrawEngineCommon::ComputeHash() {
|
||||
}
|
||||
}
|
||||
|
||||
fullhash += DoReliableHash(&uvScale[0], sizeof(uvScale[0]) * numDrawCalls, 0x0123e658);
|
||||
fullhash += DoReliableHash(&drawCalls[0].uvScale, sizeof(drawCalls[0].uvScale) * numDrawCalls, 0x0123e658);
|
||||
return fullhash;
|
||||
}
|
||||
|
||||
// vertTypeID is the vertex type but with the UVGen mode smashed into the top bits.
|
||||
void DrawEngineCommon::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertTypeID, int *bytesRead) {
|
||||
if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls >= MAX_DEFERRED_DRAW_CALLS || vertexCountInDrawCalls_ + vertexCount > VERTEX_BUFFER_MAX) {
|
||||
DispatchFlush();
|
||||
}
|
||||
|
||||
// TODO: Is this the right thing to do?
|
||||
if (prim == GE_PRIM_KEEP_PREVIOUS) {
|
||||
prim = prevPrim_ != GE_PRIM_INVALID ? prevPrim_ : GE_PRIM_POINTS;
|
||||
} else {
|
||||
prevPrim_ = prim;
|
||||
}
|
||||
|
||||
// If vtype has changed, setup the vertex decoder.
|
||||
if (vertTypeID != lastVType_) {
|
||||
dec_ = GetVertexDecoder(vertTypeID);
|
||||
lastVType_ = vertTypeID;
|
||||
}
|
||||
|
||||
*bytesRead = vertexCount * dec_->VertexSize();
|
||||
if ((vertexCount < 2 && prim > 0) || (vertexCount < 3 && prim > 2 && prim != GE_PRIM_RECTANGLES))
|
||||
return;
|
||||
|
||||
if (g_Config.bVertexCache) {
|
||||
u32 dhash = dcid_;
|
||||
dhash = __rotl(dhash ^ (u32)(uintptr_t)verts, 13);
|
||||
dhash = __rotl(dhash ^ (u32)(uintptr_t)inds, 13);
|
||||
dhash = __rotl(dhash ^ (u32)vertTypeID, 13);
|
||||
dhash = __rotl(dhash ^ (u32)vertexCount, 13);
|
||||
dcid_ = dhash ^ (u32)prim;
|
||||
}
|
||||
|
||||
DeferredDrawCall &dc = drawCalls[numDrawCalls];
|
||||
dc.verts = verts;
|
||||
dc.inds = inds;
|
||||
dc.indexType = (vertTypeID & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT;
|
||||
dc.prim = prim;
|
||||
dc.vertexCount = vertexCount;
|
||||
dc.uvScale = gstate_c.uv;
|
||||
|
||||
if (inds) {
|
||||
GetIndexBounds(inds, vertexCount, vertTypeID, &dc.indexLowerBound, &dc.indexUpperBound);
|
||||
} else {
|
||||
dc.indexLowerBound = 0;
|
||||
dc.indexUpperBound = vertexCount - 1;
|
||||
}
|
||||
|
||||
numDrawCalls++;
|
||||
vertexCountInDrawCalls_ += vertexCount;
|
||||
|
||||
if (vertTypeID & GE_VTYPE_WEIGHT_MASK) {
|
||||
DecodeVertsStep(decoded, decodeCounter_, decodedVerts_);
|
||||
decodeCounter_++;
|
||||
}
|
||||
|
||||
if (prim == GE_PRIM_RECTANGLES && (gstate.getTextureAddress(0) & 0x3FFFFFFF) == (gstate.getFrameBufAddress() & 0x3FFFFFFF)) {
|
||||
// Rendertarget == texture?
|
||||
if (!g_Config.bDisableSlowFramebufEffects) {
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);
|
||||
DispatchFlush();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,12 @@ typedef u64 ReliableHashType;
|
||||
typedef u32 ReliableHashType;
|
||||
#endif
|
||||
|
||||
inline uint32_t GetVertTypeID(uint32_t vertType, int uvGenMode) {
|
||||
// As the decoder depends on the UVGenMode when we use UV prescale, we simply mash it
|
||||
// into the top of the verttype where there are unused bits.
|
||||
return (vertType & 0xFFFFFF) | (uvGenMode << 24);
|
||||
}
|
||||
|
||||
class DrawEngineCommon {
|
||||
public:
|
||||
DrawEngineCommon();
|
||||
@@ -56,10 +62,17 @@ public:
|
||||
// Flush is normally non-virtual but here's a virtual way to call it, used by the shared spline code, which is expensive anyway.
|
||||
// Not really sure if these wrappers are worth it...
|
||||
virtual void DispatchFlush() = 0;
|
||||
// Same for SubmitPrim
|
||||
virtual void DispatchSubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) = 0;
|
||||
|
||||
// This would seem to be unnecessary now, but is still required for splines/beziers to work in the software backend since SubmitPrim
|
||||
// is different. Should probably refactor that.
|
||||
// Note that vertTypeID should be computed using GetVertTypeID().
|
||||
virtual void DispatchSubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertTypeID, int *bytesRead) {
|
||||
SubmitPrim(verts, inds, prim, vertexCount, vertTypeID, bytesRead);
|
||||
}
|
||||
|
||||
bool TestBoundingBox(void* control_points, int vertexCount, u32 vertType, int *bytesRead);
|
||||
|
||||
void SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertTypeID, int *bytesRead);
|
||||
void SubmitSpline(const void *control_points, const void *indices, int tess_u, int tess_v, int count_u, int count_v, int type_u, int type_v, GEPatchPrimType prim_type, bool computeNormals, bool patchFacing, u32 vertType, int *bytesRead);
|
||||
void SubmitBezier(const void *control_points, const void *indices, int tess_u, int tess_v, int count_u, int count_v, GEPatchPrimType prim_type, bool computeNormals, bool patchFacing, u32 vertType, int *bytesRead);
|
||||
|
||||
@@ -68,11 +81,14 @@ public:
|
||||
|
||||
virtual void Resized();
|
||||
|
||||
void SetupVertexDecoder(u32 vertType);
|
||||
|
||||
bool IsCodePtrVertexDecoder(const u8 *ptr) const {
|
||||
return decJitCache_->IsInSpace(ptr);
|
||||
}
|
||||
int GetNumDrawCalls() const {
|
||||
return numDrawCalls;
|
||||
}
|
||||
|
||||
VertexDecoder *GetVertexDecoder(u32 vtype);
|
||||
|
||||
protected:
|
||||
virtual void ClearTrackedVertexArrays() {}
|
||||
@@ -92,8 +108,6 @@ protected:
|
||||
|
||||
bool ApplyShaderBlending();
|
||||
|
||||
VertexDecoder *GetVertexDecoder(u32 vtype);
|
||||
|
||||
inline int IndexSize(u32 vtype) const {
|
||||
const u32 indexType = (vtype & GE_VTYPE_IDX_MASK);
|
||||
if (indexType == GE_VTYPE_IDX_16BIT) {
|
||||
@@ -119,23 +133,22 @@ protected:
|
||||
TransformedVertex *transformed = nullptr;
|
||||
TransformedVertex *transformedExpanded = nullptr;
|
||||
|
||||
// Defer all vertex decoding to a "Flush" (except when software skinning)
|
||||
// Defer all vertex decoding to a "Flush" (except when skinning)
|
||||
struct DeferredDrawCall {
|
||||
void *verts;
|
||||
void *inds;
|
||||
u32 vertType;
|
||||
u8 indexType;
|
||||
s8 prim;
|
||||
u32 vertexCount;
|
||||
u16 vertexCount;
|
||||
u16 indexLowerBound;
|
||||
u16 indexUpperBound;
|
||||
UVScale uvScale;
|
||||
};
|
||||
|
||||
enum { MAX_DEFERRED_DRAW_CALLS = 128 };
|
||||
DeferredDrawCall drawCalls[MAX_DEFERRED_DRAW_CALLS];
|
||||
int numDrawCalls = 0;
|
||||
int vertexCountInDrawCalls_ = 0;
|
||||
UVScale uvScale[MAX_DEFERRED_DRAW_CALLS];
|
||||
|
||||
int decimationCounter_ = 0;
|
||||
int decodeCounter_ = 0;
|
||||
@@ -170,6 +183,7 @@ protected:
|
||||
colStride = 4;
|
||||
}
|
||||
virtual void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) = 0;
|
||||
virtual void EndFrame() {}
|
||||
};
|
||||
TessellationDataTransfer *tessDataTransfer;
|
||||
};
|
||||
@@ -210,8 +210,6 @@ void FramebufferManagerCommon::SetNumExtraFBOs(int num) {
|
||||
extraFBOs_.push_back(fbo);
|
||||
}
|
||||
currentRenderVfb_ = 0;
|
||||
if (num != 0)
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
}
|
||||
|
||||
// Heuristics to figure out the size of FBO to create.
|
||||
@@ -610,6 +608,8 @@ void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffe
|
||||
// performance-crushing framebuffer reloads from RAM, but we'll have to live with that.
|
||||
if (vfb->last_frame_render != gpuStats.numFlips) {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
// GLES resets the blend state on clears.
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE);
|
||||
} else {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
}
|
||||
@@ -692,7 +692,6 @@ void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size, bool safe) {
|
||||
FlushBeforeCopy();
|
||||
|
||||
if (useBufferedRendering_ && vfb->fbo) {
|
||||
DisableState();
|
||||
GEBufferFormat fmt = vfb->format;
|
||||
if (vfb->last_frame_render + 1 < gpuStats.numFlips && isDisplayBuf) {
|
||||
// If we're not rendering to it, format may be wrong. Use displayFormat_ instead.
|
||||
@@ -720,8 +719,6 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int
|
||||
float u0 = 0.0f, u1 = 1.0f;
|
||||
float v0 = 0.0f, v1 = 1.0f;
|
||||
|
||||
MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height, u1, v1);
|
||||
|
||||
if (useBufferedRendering_ && vfb && vfb->fbo) {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
SetViewport2D(0, 0, vfb->renderWidth, vfb->renderHeight);
|
||||
@@ -735,7 +732,8 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int
|
||||
SetViewport2D(x, y, w, h);
|
||||
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
|
||||
}
|
||||
DisableState();
|
||||
|
||||
MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height, u1, v1);
|
||||
|
||||
DrawTextureFlags flags = (vfb || g_Config.iBufFilter == SCALE_LINEAR) ? DRAWTEX_LINEAR : DRAWTEX_NEAREST;
|
||||
Bind2DShader();
|
||||
@@ -777,8 +775,6 @@ void FramebufferManagerCommon::DrawFramebufferToOutput(const u8 *srcPixels, GEBu
|
||||
float v0 = 0.0f, v1 = 1.0f;
|
||||
MakePixelTexture(srcPixels, srcPixelFormat, srcStride, 512, 272, u1, v1);
|
||||
|
||||
DisableState();
|
||||
|
||||
struct CardboardSettings cardboardSettings;
|
||||
GetCardboardSettings(&cardboardSettings);
|
||||
|
||||
@@ -853,6 +849,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
|
||||
if (useBufferedRendering_) {
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
}
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -922,6 +919,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
|
||||
SetViewport2D(0, 0, pixelWidth_, pixelHeight_);
|
||||
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
|
||||
DrawFramebufferToOutput(Memory::GetPointer(displayFramebufPtr_), displayFormat_, displayStride_, true);
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@@ -931,6 +929,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
|
||||
// Bind and clear the backbuffer. This should be the first time during the frame that it's bound.
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
}
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -950,7 +949,6 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
|
||||
|
||||
if (vfb->fbo) {
|
||||
DEBUG_LOG(FRAMEBUF, "Displaying FBO %08x", vfb->fb_address);
|
||||
DisableState();
|
||||
|
||||
int uvRotation = useBufferedRendering_ ? g_Config.iInternalScreenRotation : ROTATION_LOCKED_HORIZONTAL;
|
||||
|
||||
@@ -995,7 +993,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
|
||||
int fbo_w, fbo_h;
|
||||
draw_->GetFramebufferDimensions(extraFBOs_[0], &fbo_w, &fbo_h);
|
||||
SetViewport2D(0, 0, fbo_w, fbo_h);
|
||||
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
|
||||
draw_->SetScissorRect(0, 0, fbo_w, fbo_h);
|
||||
shaderManager_->DirtyLastShader(); // dirty lastShader_
|
||||
PostShaderUniforms uniforms{};
|
||||
CalculatePostShaderUniforms(vfb->bufferWidth, vfb->bufferHeight, renderWidth_, renderHeight_, &uniforms);
|
||||
@@ -1003,8 +1001,8 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
|
||||
DrawTextureFlags flags = g_Config.iBufFilter == SCALE_LINEAR ? DRAWTEX_LINEAR : DRAWTEX_NEAREST;
|
||||
DrawActiveTexture(0, 0, fbo_w, fbo_h, fbo_w, fbo_h, 0.0f, 0.0f, 1.0f, 1.0f, ROTATION_LOCKED_HORIZONTAL, flags);
|
||||
|
||||
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
SetViewport2D(0, 0, pixelWidth_, pixelHeight_);
|
||||
|
||||
// Use the extra FBO, with applied post-processing shader, as a texture.
|
||||
// fbo_bind_as_texture(extraFBOs_[0], FB_COLOR_BIT, 0);
|
||||
@@ -1033,13 +1031,6 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
|
||||
SetViewport2D(0, 0, pixelWidth_, pixelHeight_);
|
||||
DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, uvRotation, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) {
|
||||
draw_->BindFramebufferAsRenderTarget(extraFBOs_[0], { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
GLenum attachments[3] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT };
|
||||
glInvalidateFramebuffer(GL_FRAMEBUFFER, 3, attachments);
|
||||
}*/
|
||||
} else {
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
draw_->BindFramebufferAsTexture(vfb->fbo, 0, Draw::FB_COLOR_BIT, 0);
|
||||
@@ -1072,6 +1063,9 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
|
||||
else if (useBufferedRendering_) {
|
||||
WARN_LOG(FRAMEBUF, "Current VFB lacks an FBO: %08x", vfb->fb_address);
|
||||
}
|
||||
|
||||
// This may get called mid-draw if the game uses an immediate flip.
|
||||
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE);
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::DecimateFBOs() {
|
||||
@@ -1193,6 +1187,8 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w,
|
||||
INFO_LOG(FRAMEBUF, "Resizing FBO for %08x : %d x %d x %d", vfb->fb_address, w, h, vfb->format);
|
||||
if (vfb->fbo) {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
// GLES resets the blend state on clears.
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE);
|
||||
if (!skipCopy && !g_Config.bDisableSlowFramebufEffects) {
|
||||
BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0);
|
||||
}
|
||||
@@ -1203,6 +1199,8 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w,
|
||||
}
|
||||
} else {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
// GLES resets the blend state on clears.
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE);
|
||||
}
|
||||
|
||||
if (!vfb->fbo) {
|
||||
@@ -1356,9 +1354,10 @@ void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dst
|
||||
const u32 byteOffset = dstBasePtr - vfb_address;
|
||||
const u32 byteStride = dstStride * bpp;
|
||||
const u32 yOffset = byteOffset / byteStride;
|
||||
|
||||
// Some games use mismatching bitdepths. But make sure the stride matches.
|
||||
// If it doesn't, generally this means we detected the framebuffer with too large a height.
|
||||
bool match = yOffset < dstYOffset;
|
||||
bool match = yOffset < dstYOffset && (int)yOffset <= (int)vfb->height - dstHeight;
|
||||
if (match && vfb_byteStride != byteStride) {
|
||||
// Grand Knights History copies with a mismatching stride but a full line at a time.
|
||||
// Makes it hard to detect the wrong transfers in e.g. God of War.
|
||||
@@ -1388,7 +1387,7 @@ void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dst
|
||||
const u32 byteOffset = srcBasePtr - vfb_address;
|
||||
const u32 byteStride = srcStride * bpp;
|
||||
const u32 yOffset = byteOffset / byteStride;
|
||||
bool match = yOffset < srcYOffset;
|
||||
bool match = yOffset < srcYOffset && (int)yOffset <= (int)vfb->height - srcHeight;
|
||||
if (match && vfb_byteStride != byteStride) {
|
||||
if (width != srcStride || (byteStride * height != vfb_byteStride && byteStride * height != vfb_byteWidth)) {
|
||||
match = false;
|
||||
@@ -1938,9 +1937,8 @@ bool FramebufferManagerCommon::GetFramebuffer(u32 fb_address, int fb_stride, GEB
|
||||
buffer.Allocate(w, h, GE_FORMAT_8888, flipY, true);
|
||||
bool retval = draw_->CopyFramebufferToMemorySync(bound, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), w);
|
||||
gpuStats.numReadbacks++;
|
||||
// Vulkan requires us to re-apply all dynamic state for each command buffer, and the above will cause us to start a new cmdbuf.
|
||||
// So let's dirty the things that are involved in Vulkan dynamic state. Readbacks are not frequent so this won't hurt other backends.
|
||||
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE);
|
||||
// After a readback we'll have flushed and started over, need to dirty a bunch of things to be safe.
|
||||
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);
|
||||
// We may have blitted to a temp FBO.
|
||||
RebindFramebuffer();
|
||||
return retval;
|
||||
@@ -1974,6 +1972,8 @@ bool FramebufferManagerCommon::GetDepthbuffer(u32 fb_address, int fb_stride, u32
|
||||
}
|
||||
// No need to free on failure, that's the caller's job (it likely will reuse a buffer.)
|
||||
bool retval = draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_DEPTH_BIT, 0, 0, w, h, Draw::DataFormat::D32F, buffer.GetData(), w);
|
||||
// After a readback we'll have flushed and started over, need to dirty a bunch of things to be safe.
|
||||
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);
|
||||
// That may have unbound the framebuffer, rebind to avoid crashes when debugging.
|
||||
RebindFramebuffer();
|
||||
return retval;
|
||||
@@ -2044,6 +2044,10 @@ void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int
|
||||
DEBUG_LOG(G3D, "Reading framebuffer to mem, fb_address = %08x", fb_address);
|
||||
|
||||
draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride);
|
||||
|
||||
// A new command buffer will begin after CopyFrameBufferToMemorySync, so we need to trigger
|
||||
// updates of any dynamic command buffer state by dirtying some stuff.
|
||||
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE);
|
||||
gpuStats.numReadbacks++;
|
||||
}
|
||||
|
||||
|
||||
@@ -150,6 +150,7 @@ enum DrawTextureFlags {
|
||||
DRAWTEX_NEAREST = 0,
|
||||
DRAWTEX_LINEAR = 1,
|
||||
DRAWTEX_KEEP_TEX = 2,
|
||||
DRAWTEX_KEEP_STENCIL_ALPHA = 4,
|
||||
};
|
||||
|
||||
inline Draw::DataFormat GEFormatToThin3D(int geFormat) {
|
||||
@@ -206,7 +207,7 @@ public:
|
||||
return vfb;
|
||||
}
|
||||
}
|
||||
virtual void RebindFramebuffer();
|
||||
void RebindFramebuffer();
|
||||
std::vector<FramebufferInfo> GetFramebufferList();
|
||||
|
||||
void CopyDisplayToOutput();
|
||||
@@ -222,9 +223,9 @@ public:
|
||||
bool NotifyBlockTransferBefore(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason);
|
||||
void NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason);
|
||||
|
||||
virtual void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h);
|
||||
void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h);
|
||||
|
||||
virtual void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes);
|
||||
void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes);
|
||||
void DrawFramebufferToOutput(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, bool applyPostShader);
|
||||
|
||||
void DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height);
|
||||
@@ -312,7 +313,6 @@ protected:
|
||||
bool UpdateSize();
|
||||
void SetNumExtraFBOs(int num);
|
||||
|
||||
virtual void DisableState() = 0;
|
||||
void FlushBeforeCopy();
|
||||
virtual void DecimateFBOs(); // keeping it virtual to let D3D do a little extra
|
||||
|
||||
|
||||
@@ -577,17 +577,11 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo
|
||||
|
||||
// This is a bit of a hack as the render buffer isn't always that size
|
||||
// We always scissor on non-buffered so that clears don't spill outside the frame.
|
||||
if (useBufferedRendering && scissorX1 == 0 && scissorY1 == 0
|
||||
&& scissorX2 >= (int)gstate_c.curRTWidth
|
||||
&& scissorY2 >= (int)gstate_c.curRTHeight) {
|
||||
out.scissorEnable = false;
|
||||
} else {
|
||||
out.scissorEnable = true;
|
||||
out.scissorX = renderX + displayOffsetX + scissorX1 * renderWidthFactor;
|
||||
out.scissorY = renderY + displayOffsetY + scissorY1 * renderHeightFactor;
|
||||
out.scissorW = (scissorX2 - scissorX1) * renderWidthFactor;
|
||||
out.scissorH = (scissorY2 - scissorY1) * renderHeightFactor;
|
||||
}
|
||||
out.scissorEnable = true;
|
||||
out.scissorX = renderX + displayOffsetX + scissorX1 * renderWidthFactor;
|
||||
out.scissorY = renderY + displayOffsetY + scissorY1 * renderHeightFactor;
|
||||
out.scissorW = (scissorX2 - scissorX1) * renderWidthFactor;
|
||||
out.scissorH = (scissorY2 - scissorY1) * renderHeightFactor;
|
||||
|
||||
int curRTWidth = gstate_c.curRTWidth;
|
||||
int curRTHeight = gstate_c.curRTHeight;
|
||||
|
||||
@@ -78,14 +78,8 @@ enum : uint64_t {
|
||||
DIRTY_WORLDMATRIX = 1ULL << 21,
|
||||
DIRTY_VIEWMATRIX = 1ULL << 22,
|
||||
DIRTY_TEXMATRIX = 1ULL << 23,
|
||||
DIRTY_BONEMATRIX0 = 1ULL << 24, // NOTE: These must be under 32
|
||||
DIRTY_BONEMATRIX1 = 1ULL << 25,
|
||||
DIRTY_BONEMATRIX2 = 1ULL << 26,
|
||||
DIRTY_BONEMATRIX3 = 1ULL << 27,
|
||||
DIRTY_BONEMATRIX4 = 1ULL << 28,
|
||||
DIRTY_BONEMATRIX5 = 1ULL << 29,
|
||||
DIRTY_BONEMATRIX6 = 1ULL << 30,
|
||||
DIRTY_BONEMATRIX7 = 1ULL << 31,
|
||||
|
||||
// 8 free bits here where bones used to be!
|
||||
|
||||
// These are for hardware tessellation
|
||||
DIRTY_BEZIERSPLINE = 1ULL << 32,
|
||||
@@ -93,8 +87,6 @@ enum : uint64_t {
|
||||
|
||||
// space for 7 more uniforms.
|
||||
|
||||
DIRTY_BONE_UNIFORMS = 0xFF000000ULL,
|
||||
|
||||
DIRTY_ALL_UNIFORMS = 0x3FFFFFFFFULL,
|
||||
DIRTY_ALL_LIGHTS = DIRTY_LIGHT0 | DIRTY_LIGHT1 | DIRTY_LIGHT2 | DIRTY_LIGHT3,
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ std::string VertexShaderDesc(const ShaderID &id) {
|
||||
int ls1 = id.Bits(VS_BIT_LS1, 2);
|
||||
|
||||
if (uvgMode) desc << uvgModes[uvgMode];
|
||||
if (id.Bit(VS_BIT_ENABLE_BONES)) desc << "Bones:" << (id.Bits(VS_BIT_BONES, 3) + 1) << " ";
|
||||
// Lights
|
||||
if (id.Bit(VS_BIT_LIGHTING_ENABLE)) {
|
||||
desc << "Light: ";
|
||||
@@ -46,7 +45,6 @@ std::string VertexShaderDesc(const ShaderID &id) {
|
||||
}
|
||||
}
|
||||
if (id.Bits(VS_BIT_MATERIAL_UPDATE, 3)) desc << "MatUp:" << id.Bits(VS_BIT_MATERIAL_UPDATE, 3) << " ";
|
||||
if (id.Bits(VS_BIT_WEIGHT_FMTSCALE, 2)) desc << "WScale " << id.Bits(VS_BIT_WEIGHT_FMTSCALE, 2) << " ";
|
||||
if (id.Bit(VS_BIT_FLATSHADE)) desc << "Flat ";
|
||||
|
||||
if (id.Bit(VS_BIT_BEZIER)) desc << "Bezier ";
|
||||
@@ -103,16 +101,6 @@ void ComputeVertexShaderID(ShaderID *id_out, u32 vertType, bool useHWTransform)
|
||||
id.SetBits(VS_BIT_LS1, 2, gstate.getUVLS1());
|
||||
}
|
||||
|
||||
// Bones.
|
||||
bool enableBones = vertTypeIsSkinningEnabled(vertType);
|
||||
id.SetBit(VS_BIT_ENABLE_BONES, enableBones);
|
||||
if (enableBones) {
|
||||
id.SetBits(VS_BIT_BONES, 3, TranslateNumBones(vertTypeGetNumBoneWeights(vertType)) - 1);
|
||||
// 2 bits. We should probably send in the weight scalefactor as a uniform instead,
|
||||
// or simply preconvert all weights to floats.
|
||||
id.SetBits(VS_BIT_WEIGHT_FMTSCALE, 2, (vertType & GE_VTYPE_WEIGHT_MASK) >> GE_VTYPE_WEIGHT_SHIFT);
|
||||
}
|
||||
|
||||
// Okay, d[1] coming up. ==============
|
||||
if (gstate.isLightingEnabled() || doShadeMapping) {
|
||||
// doShadeMapping is stored as UVGenMode, so this is enough for isLightingEnabled.
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
// TODO: There will be additional bits, indicating that groups of these will be
|
||||
// sent to the shader and processed there. This will cut down the number of shaders ("ubershader approach")
|
||||
// This is probably only really worth doing for lighting and bones.
|
||||
enum {
|
||||
VS_BIT_LMODE = 0,
|
||||
VS_BIT_IS_THROUGH = 1,
|
||||
@@ -29,10 +28,7 @@ enum {
|
||||
VS_BIT_UVPROJ_MODE = 18, // 2, can overlap with LS0
|
||||
VS_BIT_LS0 = 18, // 2
|
||||
VS_BIT_LS1 = 20, // 2
|
||||
VS_BIT_BONES = 22, // 3 should be enough, not 8
|
||||
// 25 - 29 are free.
|
||||
VS_BIT_ENABLE_BONES = 30,
|
||||
// 31 is free.
|
||||
// 22 - 31 are free.
|
||||
VS_BIT_LIGHT0_COMP = 32, // 2 bits
|
||||
VS_BIT_LIGHT0_TYPE = 34, // 2 bits
|
||||
VS_BIT_LIGHT1_COMP = 36, // 2 bits
|
||||
@@ -48,8 +44,7 @@ enum {
|
||||
VS_BIT_LIGHT2_ENABLE = 54,
|
||||
VS_BIT_LIGHT3_ENABLE = 55,
|
||||
VS_BIT_LIGHTING_ENABLE = 56,
|
||||
VS_BIT_WEIGHT_FMTSCALE = 57, // only two bits
|
||||
// 59 - 61 are free.
|
||||
// 57 - 61 are free.
|
||||
VS_BIT_FLATSHADE = 62, // 1 bit
|
||||
VS_BIT_BEZIER = 63, // 1 bit
|
||||
// No more free
|
||||
|
||||
@@ -262,19 +262,11 @@ void LightUpdateUniforms(UB_VS_Lights *ub, uint64_t dirtyUniforms) {
|
||||
}
|
||||
ExpandFloat24x3ToFloat4(ub->ldir[i], &gstate.ldir[i * 3]);
|
||||
ExpandFloat24x3ToFloat4(ub->latt[i], &gstate.latt[i * 3]);
|
||||
CopyFloat1To4(ub->lightAngle[i], getFloat24(gstate.lcutoff[i]));
|
||||
CopyFloat1To4(ub->lightSpotCoef[i], getFloat24(gstate.lconv[i]));
|
||||
float lightAngle_spotCoef[2] = { getFloat24(gstate.lcutoff[i]), getFloat24(gstate.lconv[i]) };
|
||||
CopyFloat2To4(ub->lightAngle_SpotCoef[i], lightAngle_spotCoef);
|
||||
Uint8x3ToFloat4(ub->lightAmbient[i], gstate.lcolor[i * 3]);
|
||||
Uint8x3ToFloat4(ub->lightDiffuse[i], gstate.lcolor[i * 3 + 1]);
|
||||
Uint8x3ToFloat4(ub->lightSpecular[i], gstate.lcolor[i * 3 + 2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BoneUpdateUniforms(UB_VS_Bones *ub, uint64_t dirtyUniforms) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (dirtyUniforms & (DIRTY_BONEMATRIX0 << i)) {
|
||||
ConvertMatrix4x3To3x4Transposed(ub->bones[i], gstate.boneMatrix + 12 * i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,8 @@ R"( float4x4 u_proj;
|
||||
float2 u_texclampoff;
|
||||
)";
|
||||
|
||||
// 576 bytes. Can we get down to 512?
|
||||
// 512 bytes. Would like to shrink more. Some colors only have 8-bit precision and we expand
|
||||
// them to float unnecessarily, could just as well expand in the shader.
|
||||
struct UB_VS_Lights {
|
||||
float ambientColor[4];
|
||||
float materialDiffuse[4];
|
||||
@@ -107,8 +108,7 @@ struct UB_VS_Lights {
|
||||
float lpos[4][4];
|
||||
float ldir[4][4];
|
||||
float latt[4][4];
|
||||
float lightAngle[4][4]; // TODO: Merge with lightSpotCoef, use .xy
|
||||
float lightSpotCoef[4][4];
|
||||
float lightAngle_SpotCoef[4][4]; // TODO: Merge with lightSpotCoef, use .xy
|
||||
float lightAmbient[4][4];
|
||||
float lightDiffuse[4][4];
|
||||
float lightSpecular[4][4];
|
||||
@@ -122,8 +122,7 @@ R"( vec4 u_ambient;
|
||||
vec3 pos[4];
|
||||
vec3 dir[4];
|
||||
vec3 att[4];
|
||||
float angle[4];
|
||||
float spotCoef[4];
|
||||
vec2 angle_spotCoef[4];
|
||||
vec3 ambient[4];
|
||||
vec3 diffuse[4];
|
||||
vec3 specular[4];
|
||||
@@ -147,14 +146,10 @@ R"( float4 u_ambient;
|
||||
float3 u_lightatt1;
|
||||
float3 u_lightatt2;
|
||||
float3 u_lightatt3;
|
||||
float4 u_lightangle0;
|
||||
float4 u_lightangle1;
|
||||
float4 u_lightangle2;
|
||||
float4 u_lightangle3;
|
||||
float4 u_lightspotCoef0;
|
||||
float4 u_lightspotCoef1;
|
||||
float4 u_lightspotCoef2;
|
||||
float4 u_lightspotCoef3;
|
||||
float4 u_lightangle_spotCoef0;
|
||||
float4 u_lightangle_spotCoef1;
|
||||
float4 u_lightangle_spotCoef2;
|
||||
float4 u_lightangle_spotCoef3;
|
||||
float3 u_lightambient0;
|
||||
float3 u_lightambient1;
|
||||
float3 u_lightambient2;
|
||||
@@ -169,24 +164,8 @@ R"( float4 u_ambient;
|
||||
float3 u_lightspecular3;
|
||||
)";
|
||||
|
||||
// With some cleverness, we could get away with uploading just half this when only the four or five first
|
||||
// bones are being used. This is 512b, 256b would be great.
|
||||
struct UB_VS_Bones {
|
||||
float bones[8][12];
|
||||
};
|
||||
|
||||
static const char *ub_vs_bonesStr =
|
||||
R"( mat3x4 m[8];
|
||||
)";
|
||||
|
||||
// HLSL code is shared so these names are changed to match those in DX9.
|
||||
static const char *cb_vs_bonesStr =
|
||||
R"( float4x3 u_bone[8];
|
||||
)";
|
||||
|
||||
void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipViewport);
|
||||
void LightUpdateUniforms(UB_VS_Lights *ub, uint64_t dirtyUniforms);
|
||||
void BoneUpdateUniforms(UB_VS_Bones *ub, uint64_t dirtyUniforms);
|
||||
|
||||
// Shared helper functions
|
||||
void ComputeGuardband(float gb[4], float zmin);
|
||||
@@ -144,8 +144,6 @@ void SoftwareTransform(
|
||||
vscale /= gstate_c.curTextureHeight;
|
||||
}
|
||||
|
||||
bool skinningEnabled = vertTypeIsSkinningEnabled(vertType);
|
||||
|
||||
const int w = gstate.getTextureWidth(0);
|
||||
const int h = gstate.getTextureHeight(0);
|
||||
float widthFactor = (float) w / (float) gstate_c.curTextureWidth;
|
||||
@@ -213,48 +211,14 @@ void SoftwareTransform(
|
||||
Vec3f worldnormal(0, 0, 1);
|
||||
reader.ReadPos(pos);
|
||||
|
||||
if (!skinningEnabled) {
|
||||
Vec3ByMatrix43(out, pos, gstate.worldMatrix);
|
||||
if (reader.hasNormal()) {
|
||||
reader.ReadNrm(normal.AsArray());
|
||||
if (gstate.areNormalsReversed()) {
|
||||
normal = -normal;
|
||||
}
|
||||
Norm3ByMatrix43(worldnormal.AsArray(), normal.AsArray(), gstate.worldMatrix);
|
||||
worldnormal = worldnormal.Normalized();
|
||||
}
|
||||
} else {
|
||||
float weights[8];
|
||||
reader.ReadWeights(weights);
|
||||
if (reader.hasNormal())
|
||||
reader.ReadNrm(normal.AsArray());
|
||||
|
||||
// Skinning
|
||||
Vec3f psum(0, 0, 0);
|
||||
Vec3f nsum(0, 0, 0);
|
||||
for (int i = 0; i < vertTypeGetNumBoneWeights(vertType); i++) {
|
||||
if (weights[i] != 0.0f) {
|
||||
Vec3ByMatrix43(out, pos, gstate.boneMatrix+i*12);
|
||||
Vec3f tpos(out);
|
||||
psum += tpos * weights[i];
|
||||
if (reader.hasNormal()) {
|
||||
Vec3f norm;
|
||||
Norm3ByMatrix43(norm.AsArray(), normal.AsArray(), gstate.boneMatrix+i*12);
|
||||
nsum += norm * weights[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Yes, we really must multiply by the world matrix too.
|
||||
Vec3ByMatrix43(out, psum.AsArray(), gstate.worldMatrix);
|
||||
if (reader.hasNormal()) {
|
||||
normal = nsum;
|
||||
if (gstate.areNormalsReversed()) {
|
||||
normal = -normal;
|
||||
}
|
||||
Norm3ByMatrix43(worldnormal.AsArray(), normal.AsArray(), gstate.worldMatrix);
|
||||
worldnormal = worldnormal.Normalized();
|
||||
Vec3ByMatrix43(out, pos, gstate.worldMatrix);
|
||||
if (reader.hasNormal()) {
|
||||
reader.ReadNrm(normal.AsArray());
|
||||
if (gstate.areNormalsReversed()) {
|
||||
normal = -normal;
|
||||
}
|
||||
Norm3ByMatrix43(worldnormal.AsArray(), normal.AsArray(), gstate.worldMatrix);
|
||||
worldnormal = worldnormal.Normalized();
|
||||
}
|
||||
|
||||
// Perform lighting here if enabled. don't need to check through, it's checked above.
|
||||
|
||||
@@ -949,8 +949,10 @@ void DrawEngineCommon::SubmitSpline(const void *control_points, const void *indi
|
||||
gstate_c.uv.vOff = 0.0f;
|
||||
}
|
||||
|
||||
uint32_t vertTypeID = GetVertTypeID(vertTypeWithIndex16, gstate.getUVGenMode());
|
||||
|
||||
int generatedBytesRead;
|
||||
DispatchSubmitPrim(splineBuffer, quadIndices_, primType[prim_type], count, vertTypeWithIndex16, &generatedBytesRead);
|
||||
DispatchSubmitPrim(splineBuffer, quadIndices_, primType[prim_type], count, vertTypeID, &generatedBytesRead);
|
||||
|
||||
DispatchFlush();
|
||||
|
||||
@@ -1091,8 +1093,9 @@ void DrawEngineCommon::SubmitBezier(const void *control_points, const void *indi
|
||||
gstate_c.uv.vOff = 0;
|
||||
}
|
||||
|
||||
uint32_t vertTypeID = GetVertTypeID(vertTypeWithIndex16, gstate.getUVGenMode());
|
||||
int generatedBytesRead;
|
||||
DispatchSubmitPrim(splineBuffer, quadIndices_, primType[prim_type], count, vertTypeWithIndex16, &generatedBytesRead);
|
||||
DispatchSubmitPrim(splineBuffer, quadIndices_, primType[prim_type], count, vertTypeID, &generatedBytesRead);
|
||||
|
||||
DispatchFlush();
|
||||
|
||||
|
||||
@@ -47,6 +47,10 @@
|
||||
#define TEXTURE_KILL_AGE_LOWMEM 60
|
||||
// Not used in lowmem mode.
|
||||
#define TEXTURE_SECOND_KILL_AGE 100
|
||||
// Used when there are multiple CLUT variants of a texture.
|
||||
#define TEXTURE_KILL_AGE_CLUT 6
|
||||
|
||||
#define TEXTURE_CLUT_VARIANTS_MIN 6
|
||||
|
||||
// Try to be prime to other decimation intervals.
|
||||
#define TEXCACHE_DECIMATION_INTERVAL 13
|
||||
@@ -384,7 +388,7 @@ void TextureCacheCommon::SetTexture(bool force) {
|
||||
// Exponential backoff up to 512 frames. Textures are often reused.
|
||||
if (entry->numFrames > 32) {
|
||||
// Also, try to add some "randomness" to avoid rehashing several textures the same frame.
|
||||
entry->framesUntilNextFullHash = std::min(512, entry->numFrames) + (entry->textureName & 15);
|
||||
entry->framesUntilNextFullHash = std::min(512, entry->numFrames) + (((intptr_t)(entry->textureName) >> 12) & 15);
|
||||
} else {
|
||||
entry->framesUntilNextFullHash = entry->numFrames;
|
||||
}
|
||||
@@ -455,6 +459,24 @@ void TextureCacheCommon::SetTexture(bool force) {
|
||||
entry->status = TexCacheEntry::STATUS_UNRELIABLE;
|
||||
}
|
||||
|
||||
if (hasClut && clutRenderAddress_ == 0xFFFFFFFF) {
|
||||
const u64 cachekeyMin = (u64)(texaddr & 0x3FFFFFFF) << 32;
|
||||
const u64 cachekeyMax = cachekeyMin + (1ULL << 32);
|
||||
|
||||
int found = 0;
|
||||
for (auto it = cache_.lower_bound(cachekeyMin), end = cache_.upper_bound(cachekeyMax); it != end; ++it) {
|
||||
found++;
|
||||
}
|
||||
|
||||
if (found >= TEXTURE_CLUT_VARIANTS_MIN) {
|
||||
for (auto it = cache_.lower_bound(cachekeyMin), end = cache_.upper_bound(cachekeyMax); it != end; ++it) {
|
||||
it->second->status |= TexCacheEntry::STATUS_CLUT_VARIANTS;
|
||||
}
|
||||
|
||||
entry->status |= TexCacheEntry::STATUS_CLUT_VARIANTS;
|
||||
}
|
||||
}
|
||||
|
||||
nextNeedsChange_ = false;
|
||||
}
|
||||
|
||||
@@ -506,8 +528,10 @@ void TextureCacheCommon::Decimate() {
|
||||
const u32 had = cacheSizeEstimate_;
|
||||
|
||||
ForgetLastTexture();
|
||||
int killAge = lowMemoryMode_ ? TEXTURE_KILL_AGE_LOWMEM : TEXTURE_KILL_AGE;
|
||||
int killAgeBase = lowMemoryMode_ ? TEXTURE_KILL_AGE_LOWMEM : TEXTURE_KILL_AGE;
|
||||
for (TexCache::iterator iter = cache_.begin(); iter != cache_.end(); ) {
|
||||
bool hasClut = (iter->second->status & TexCacheEntry::STATUS_CLUT_VARIANTS) != 0;
|
||||
int killAge = hasClut ? TEXTURE_KILL_AGE_CLUT : killAgeBase;
|
||||
if (iter->second->lastFrame + killAge < gpuStats.numFlips) {
|
||||
DeleteTexture(iter++);
|
||||
} else {
|
||||
|
||||
@@ -87,6 +87,8 @@ struct SamplerCacheKey {
|
||||
}
|
||||
};
|
||||
|
||||
class GLRTexture;
|
||||
|
||||
// TODO: Shrink this struct. There is some fluff.
|
||||
struct TexCacheEntry {
|
||||
~TexCacheEntry() {
|
||||
@@ -106,8 +108,7 @@ struct TexCacheEntry {
|
||||
STATUS_ALPHA_FULL = 0x00, // Has no alpha channel, or always full alpha.
|
||||
STATUS_ALPHA_MASK = 0x04,
|
||||
|
||||
// 0x08 free.
|
||||
|
||||
STATUS_CLUT_VARIANTS = 0x08, // Has multiple CLUT variants.
|
||||
STATUS_CHANGE_FREQUENT = 0x10, // Changes often (less than 6 frames in between.)
|
||||
STATUS_CLUT_RECHECK = 0x20, // Another texture with same addr had a hashfail.
|
||||
STATUS_DEPALETTIZE = 0x40, // Needs to go through a depalettize pass.
|
||||
@@ -132,7 +133,7 @@ struct TexCacheEntry {
|
||||
u16 dim;
|
||||
u16 bufw;
|
||||
union {
|
||||
u32 textureName;
|
||||
GLRTexture *textureName;
|
||||
void *texturePtr;
|
||||
CachedTextureVulkan *vkTex;
|
||||
};
|
||||
@@ -146,16 +147,8 @@ struct TexCacheEntry {
|
||||
u32 framesUntilNextFullHash;
|
||||
u32 fullhash;
|
||||
u32 cluthash;
|
||||
float lodBias;
|
||||
u16 maxSeenV;
|
||||
|
||||
// Cache the current filter settings so we can avoid setting it again.
|
||||
// (OpenGL ES 2.0 madness where filter settings are attached to each texture. Unused in other backends).
|
||||
u8 magFilt;
|
||||
u8 minFilt;
|
||||
bool sClamp;
|
||||
bool tClamp;
|
||||
|
||||
TexStatus GetHashStatus() {
|
||||
return TexStatus(status & STATUS_MASK);
|
||||
}
|
||||
|
||||
@@ -111,9 +111,6 @@ static const ARMReg srcNEON = Q2;
|
||||
static const ARMReg accNEON = Q3;
|
||||
|
||||
static const JitLookup jitLookup[] = {
|
||||
{&VertexDecoder::Step_WeightsU8, &VertexDecoderJitCache::Jit_WeightsU8},
|
||||
{&VertexDecoder::Step_WeightsU16, &VertexDecoderJitCache::Jit_WeightsU16},
|
||||
{&VertexDecoder::Step_WeightsFloat, &VertexDecoderJitCache::Jit_WeightsFloat},
|
||||
{&VertexDecoder::Step_WeightsU8Skin, &VertexDecoderJitCache::Jit_WeightsU8Skin},
|
||||
{&VertexDecoder::Step_WeightsU16Skin, &VertexDecoderJitCache::Jit_WeightsU16Skin},
|
||||
{&VertexDecoder::Step_WeightsFloatSkin, &VertexDecoderJitCache::Jit_WeightsFloatSkin},
|
||||
@@ -232,7 +229,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int
|
||||
// Add code to convert matrices to 4x4.
|
||||
// Later we might want to do this when the matrices are loaded instead.
|
||||
int boneCount = 0;
|
||||
if (NEONSkinning && dec.weighttype && g_Config.bSoftwareSkinning && dec.morphcount == 1) {
|
||||
if (NEONSkinning && dec.weighttype) {
|
||||
// Copying from R3 to R4
|
||||
MOVP2R(R3, gstate.boneMatrix);
|
||||
MOVP2R(R4, bones);
|
||||
@@ -326,55 +323,6 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int
|
||||
return (JittedVertexDecoder)start;
|
||||
}
|
||||
|
||||
void VertexDecoderJitCache::Jit_WeightsU8() {
|
||||
// Basic implementation - a byte at a time. TODO: Optimize
|
||||
int j;
|
||||
for (j = 0; j < dec_->nweights; j++) {
|
||||
LDRB(tempReg1, srcReg, dec_->weightoff + j);
|
||||
STRB(tempReg1, dstReg, dec_->decFmt.w0off + j);
|
||||
}
|
||||
if (j & 3) {
|
||||
// Create a zero register. Might want to make a fixed one.
|
||||
EOR(scratchReg, scratchReg, scratchReg);
|
||||
}
|
||||
while (j & 3) {
|
||||
STRB(scratchReg, dstReg, dec_->decFmt.w0off + j);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
void VertexDecoderJitCache::Jit_WeightsU16() {
|
||||
// Basic implementation - a short at a time. TODO: Optimize
|
||||
int j;
|
||||
for (j = 0; j < dec_->nweights; j++) {
|
||||
LDRH(tempReg1, srcReg, dec_->weightoff + j * 2);
|
||||
STRH(tempReg1, dstReg, dec_->decFmt.w0off + j * 2);
|
||||
}
|
||||
if (j & 3) {
|
||||
// Create a zero register. Might want to make a fixed one.
|
||||
EOR(scratchReg, scratchReg, scratchReg);
|
||||
}
|
||||
while (j & 3) {
|
||||
STRH(scratchReg, dstReg, dec_->decFmt.w0off + j * 2);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
void VertexDecoderJitCache::Jit_WeightsFloat() {
|
||||
int j;
|
||||
for (j = 0; j < dec_->nweights; j++) {
|
||||
LDR(tempReg1, srcReg, dec_->weightoff + j * 4);
|
||||
STR(tempReg1, dstReg, dec_->decFmt.w0off + j * 4);
|
||||
}
|
||||
if (j & 3) {
|
||||
EOR(tempReg1, tempReg1, tempReg1);
|
||||
}
|
||||
while (j & 3) { // Zero additional weights rounding up to 4.
|
||||
STR(tempReg1, dstReg, dec_->decFmt.w0off + j * 4);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
static const ARMReg weightRegs[8] = { S8, S9, S10, S11, S12, S13, S14, S15 };
|
||||
static const ARMReg neonWeightRegsD[4] = { D4, D5, D6, D7 };
|
||||
static const ARMReg neonWeightRegsQ[2] = { Q2, Q3 };
|
||||
|
||||
@@ -85,9 +85,6 @@ static const ARM64Reg neonWeightRegsQ[2] = { Q3, Q2 }; // reverse order to prev
|
||||
// Q16+ are free-for-all for matrices. In 16 registers, we can fit 4 4x4 matrices.
|
||||
|
||||
static const JitLookup jitLookup[] = {
|
||||
{&VertexDecoder::Step_WeightsU8, &VertexDecoderJitCache::Jit_WeightsU8},
|
||||
{&VertexDecoder::Step_WeightsU16, &VertexDecoderJitCache::Jit_WeightsU16},
|
||||
{&VertexDecoder::Step_WeightsFloat, &VertexDecoderJitCache::Jit_WeightsFloat},
|
||||
{&VertexDecoder::Step_WeightsU8Skin, &VertexDecoderJitCache::Jit_WeightsU8Skin},
|
||||
{&VertexDecoder::Step_WeightsU16Skin, &VertexDecoderJitCache::Jit_WeightsU16Skin},
|
||||
{&VertexDecoder::Step_WeightsFloatSkin, &VertexDecoderJitCache::Jit_WeightsFloatSkin},
|
||||
@@ -196,7 +193,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int
|
||||
// Add code to convert matrices to 4x4.
|
||||
// Later we might want to do this when the matrices are loaded instead.
|
||||
int boneCount = 0;
|
||||
if (dec.weighttype && g_Config.bSoftwareSkinning && dec.morphcount == 1) {
|
||||
if (dec.weighttype) {
|
||||
// Copying from R3 to R4
|
||||
MOVP2R(X3, gstate.boneMatrix);
|
||||
MOVP2R(X4, bones);
|
||||
@@ -356,44 +353,6 @@ void VertexDecoderJitCache::Jit_ApplyWeights() {
|
||||
}
|
||||
}
|
||||
|
||||
void VertexDecoderJitCache::Jit_WeightsU8() {
|
||||
// Basic implementation - a byte at a time. TODO: Optimize
|
||||
int j;
|
||||
for (j = 0; j < dec_->nweights; j++) {
|
||||
LDRB(INDEX_UNSIGNED, tempReg1, srcReg, dec_->weightoff + j);
|
||||
STRB(INDEX_UNSIGNED, tempReg1, dstReg, dec_->decFmt.w0off + j);
|
||||
}
|
||||
while (j & 3) {
|
||||
STRB(INDEX_UNSIGNED, WZR, dstReg, dec_->decFmt.w0off + j);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
void VertexDecoderJitCache::Jit_WeightsU16() {
|
||||
// Basic implementation - a short at a time. TODO: Optimize
|
||||
int j;
|
||||
for (j = 0; j < dec_->nweights; j++) {
|
||||
LDRH(INDEX_UNSIGNED, tempReg1, srcReg, dec_->weightoff + j * 2);
|
||||
STRH(INDEX_UNSIGNED, tempReg1, dstReg, dec_->decFmt.w0off + j * 2);
|
||||
}
|
||||
while (j & 3) {
|
||||
STRH(INDEX_UNSIGNED, WZR, dstReg, dec_->decFmt.w0off + j * 2);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
void VertexDecoderJitCache::Jit_WeightsFloat() {
|
||||
int j;
|
||||
for (j = 0; j < dec_->nweights; j++) {
|
||||
LDR(INDEX_UNSIGNED, tempReg1, srcReg, dec_->weightoff + j * 4);
|
||||
STR(INDEX_UNSIGNED, tempReg1, dstReg, dec_->decFmt.w0off + j * 4);
|
||||
}
|
||||
while (j & 3) { // Zero additional weights rounding up to 4.
|
||||
STR(INDEX_UNSIGNED, WZR, dstReg, dec_->decFmt.w0off + j * 4);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
void VertexDecoderJitCache::Jit_WeightsU8Skin() {
|
||||
// Weight is first so srcReg is correct.
|
||||
switch (dec_->nweights) {
|
||||
|
||||
+137
-160
@@ -41,7 +41,7 @@ static const u8 nrmsize[4] = { 0, 3, 6, 12 }, nrmalign[4] = { 0, 1, 2, 4 };
|
||||
static const u8 possize[4] = { 3, 3, 6, 12 }, posalign[4] = { 1, 1, 2, 4 };
|
||||
static const u8 wtsize[4] = { 0, 1, 2, 4 }, wtalign[4] = { 0, 1, 2, 4 };
|
||||
|
||||
// When software skinning. This array is only used when non-jitted - when jitted, the matrix
|
||||
// This array is only used when non-jitted - when jitted, the matrix
|
||||
// is kept in registers.
|
||||
alignas(16) static float skinMatrix[12];
|
||||
|
||||
@@ -49,13 +49,6 @@ inline int align(int n, int align) {
|
||||
return (n + (align - 1)) & ~(align - 1);
|
||||
}
|
||||
|
||||
int TranslateNumBones(int bones) {
|
||||
if (!bones) return 0;
|
||||
if (bones < 4) return 4;
|
||||
// if (bones < 8) return 8; I get drawing problems in FF:CC with this!
|
||||
return bones;
|
||||
}
|
||||
|
||||
int DecFmtSize(u8 fmt) {
|
||||
switch (fmt) {
|
||||
case DEC_NONE: return 0;
|
||||
@@ -79,7 +72,22 @@ int DecFmtSize(u8 fmt) {
|
||||
}
|
||||
|
||||
void DecVtxFormat::ComputeID() {
|
||||
id = w0fmt | (w1fmt << 4) | (uvfmt << 8) | (c0fmt << 12) | (c1fmt << 16) | (nrmfmt << 20) | (posfmt << 24);
|
||||
id = uvfmt | (c0fmt << 4) | (c1fmt << 8) | (nrmfmt << 12) | (posfmt << 16);
|
||||
}
|
||||
|
||||
void DecVtxFormat::InitializeFromID(uint32_t id) {
|
||||
this->id = id;
|
||||
uvfmt = (id & 0xF);
|
||||
c0fmt = ((id >> 4) & 0xF);
|
||||
c1fmt = ((id >> 8) & 0xF);
|
||||
nrmfmt = ((id >> 12) & 0xF);
|
||||
posfmt = ((id >> 16) & 0xF);
|
||||
uvoff = 0;
|
||||
c0off = uvoff + DecFmtSize(uvfmt);
|
||||
c1off = c0off + DecFmtSize(c0fmt);
|
||||
nrmoff = c1off + DecFmtSize(c1fmt);
|
||||
posoff = nrmoff + DecFmtSize(nrmfmt);
|
||||
stride = posoff + DecFmtSize(posfmt);
|
||||
}
|
||||
|
||||
void GetIndexBounds(const void *inds, int count, u32 vertType, u16 *indexLowerBound, u16 *indexUpperBound) {
|
||||
@@ -158,113 +166,37 @@ void PrintDecodedVertex(VertexReader &vtx) {
|
||||
VertexDecoder::VertexDecoder() : decoded_(nullptr), ptr_(nullptr), jitted_(0), jittedSize_(0) {
|
||||
}
|
||||
|
||||
void VertexDecoder::Step_WeightsU8() const
|
||||
{
|
||||
u8 *wt = (u8 *)(decoded_ + decFmt.w0off);
|
||||
const u8 *wdata = (const u8*)(ptr_);
|
||||
int j;
|
||||
for (j = 0; j < nweights; j++)
|
||||
wt[j] = wdata[j];
|
||||
while (j & 3) // Zero additional weights rounding up to 4.
|
||||
wt[j++] = 0;
|
||||
}
|
||||
|
||||
void VertexDecoder::Step_WeightsU16() const
|
||||
{
|
||||
u16 *wt = (u16 *)(decoded_ + decFmt.w0off);
|
||||
const u16 *wdata = (const u16*)(ptr_);
|
||||
int j;
|
||||
for (j = 0; j < nweights; j++)
|
||||
wt[j] = wdata[j];
|
||||
while (j & 3) // Zero additional weights rounding up to 4.
|
||||
wt[j++] = 0;
|
||||
}
|
||||
|
||||
void VertexDecoder::Step_WeightsU8ToFloat() const
|
||||
{
|
||||
float *wt = (float *)(decoded_ + decFmt.w0off);
|
||||
const u8 *wdata = (const u8*)(ptr_);
|
||||
int j;
|
||||
for (j = 0; j < nweights; j++) {
|
||||
wt[j] = (float)wdata[j] * (1.0f / 128.0f);
|
||||
void VertexDecoder::ComputeSkinMatrix(const float weights[8]) const {
|
||||
memset(skinMatrix, 0, sizeof(skinMatrix));
|
||||
for (int j = 0; j < nweights; j++) {
|
||||
const float *bone = &gstate.boneMatrix[j * 12];
|
||||
if (weights[j] != 0.0f) {
|
||||
for (int i = 0; i < 12; i++) {
|
||||
skinMatrix[i] += weights[j] * bone[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
while (j & 3) // Zero additional weights rounding up to 4.
|
||||
wt[j++] = 0;
|
||||
}
|
||||
|
||||
void VertexDecoder::Step_WeightsU16ToFloat() const
|
||||
{
|
||||
float *wt = (float *)(decoded_ + decFmt.w0off);
|
||||
void VertexDecoder::Step_WeightsU8Skin() const {
|
||||
const u8 *wdata = (const u8*)(ptr_);
|
||||
float weights[8];
|
||||
for (int j = 0; j < nweights; j++)
|
||||
weights[j] = wdata[j] * (1.0f / 128.0f);
|
||||
ComputeSkinMatrix(weights);
|
||||
}
|
||||
|
||||
void VertexDecoder::Step_WeightsU16Skin() const {
|
||||
const u16 *wdata = (const u16*)(ptr_);
|
||||
int j;
|
||||
for (j = 0; j < nweights; j++) {
|
||||
wt[j] = (float)wdata[j] * (1.0f / 32768.0f);
|
||||
}
|
||||
while (j & 3) // Zero additional weights rounding up to 4.
|
||||
wt[j++] = 0;
|
||||
float weights[8];
|
||||
for (int j = 0; j < nweights; j++)
|
||||
weights[j] = wdata[j] * (1.0f / 32768.0f);
|
||||
ComputeSkinMatrix(weights);
|
||||
}
|
||||
|
||||
// Float weights should be uncommon, we can live with having to multiply these by 2.0
|
||||
// to avoid special checks in the vertex shader generator.
|
||||
// (PSP uses 0.0-2.0 fixed point numbers for weights)
|
||||
void VertexDecoder::Step_WeightsFloat() const
|
||||
{
|
||||
float *wt = (float *)(decoded_ + decFmt.w0off);
|
||||
void VertexDecoder::Step_WeightsFloatSkin() const {
|
||||
const float *wdata = (const float*)(ptr_);
|
||||
int j;
|
||||
for (j = 0; j < nweights; j++) {
|
||||
wt[j] = wdata[j];
|
||||
}
|
||||
while (j & 3) // Zero additional weights rounding up to 4.
|
||||
wt[j++] = 0.0f;
|
||||
}
|
||||
|
||||
void VertexDecoder::Step_WeightsU8Skin() const
|
||||
{
|
||||
memset(skinMatrix, 0, sizeof(skinMatrix));
|
||||
const u8 *wdata = (const u8*)(ptr_);
|
||||
for (int j = 0; j < nweights; j++) {
|
||||
const float *bone = &gstate.boneMatrix[j * 12];
|
||||
if (wdata[j] != 0) {
|
||||
float weight = wdata[j] * (1.0f / 128.0f);
|
||||
for (int i = 0; i < 12; i++) {
|
||||
skinMatrix[i] += weight * bone[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VertexDecoder::Step_WeightsU16Skin() const
|
||||
{
|
||||
memset(skinMatrix, 0, sizeof(skinMatrix));
|
||||
const u16 *wdata = (const u16*)(ptr_);
|
||||
for (int j = 0; j < nweights; j++) {
|
||||
const float *bone = &gstate.boneMatrix[j * 12];
|
||||
if (wdata[j] != 0) {
|
||||
float weight = wdata[j] * (1.0f / 32768.0f);
|
||||
for (int i = 0; i < 12; i++) {
|
||||
skinMatrix[i] += weight * bone[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Float weights should be uncommon, we can live with having to multiply these by 2.0
|
||||
// to avoid special checks in the vertex shader generator.
|
||||
// (PSP uses 0.0-2.0 fixed point numbers for weights)
|
||||
void VertexDecoder::Step_WeightsFloatSkin() const
|
||||
{
|
||||
memset(skinMatrix, 0, sizeof(skinMatrix));
|
||||
const float *wdata = (const float*)(ptr_);
|
||||
for (int j = 0; j < nweights; j++) {
|
||||
const float *bone = &gstate.boneMatrix[j * 12];
|
||||
float weight = wdata[j];
|
||||
if (weight > 0.0) {
|
||||
for (int i = 0; i < 12; i++) {
|
||||
skinMatrix[i] += weight * bone[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
ComputeSkinMatrix(wdata);
|
||||
}
|
||||
|
||||
void VertexDecoder::Step_TcU8ToFloat() const
|
||||
@@ -688,6 +620,42 @@ void VertexDecoder::Step_NormalFloatMorph() const
|
||||
}
|
||||
}
|
||||
|
||||
void VertexDecoder::Step_NormalS8MorphSkin() const {
|
||||
float *normal = (float *)(decoded_ + decFmt.nrmoff);
|
||||
float nrm[3]{};
|
||||
for (int n = 0; n < morphcount; n++) {
|
||||
const s8 *bv = (const s8*)(ptr_ + onesize_ * n + nrmoff);
|
||||
const float multiplier = gstate_c.morphWeights[n] * (1.0f / 128.0f);
|
||||
for (int j = 0; j < 3; j++)
|
||||
normal[j] += bv[j] * multiplier;
|
||||
}
|
||||
Norm3ByMatrix43(normal, nrm, skinMatrix);
|
||||
}
|
||||
|
||||
void VertexDecoder::Step_NormalS16MorphSkin() const {
|
||||
float *normal = (float *)(decoded_ + decFmt.nrmoff);
|
||||
float nrm[3]{};
|
||||
for (int n = 0; n < morphcount; n++) {
|
||||
const s16 *sv = (const s16_le *)(ptr_ + onesize_ * n + nrmoff);
|
||||
const float multiplier = gstate_c.morphWeights[n] * (1.0f / 32768.0f);
|
||||
for (int j = 0; j < 3; j++)
|
||||
nrm[j] += sv[j] * multiplier;
|
||||
}
|
||||
Norm3ByMatrix43(normal, nrm, skinMatrix);
|
||||
}
|
||||
|
||||
void VertexDecoder::Step_NormalFloatMorphSkin() const {
|
||||
float *normal = (float *)(decoded_ + decFmt.nrmoff);
|
||||
float nrm[3]{};
|
||||
for (int n = 0; n < morphcount; n++) {
|
||||
float multiplier = gstate_c.morphWeights[n];
|
||||
const float *fv = (const float*)(ptr_ + onesize_ * n + nrmoff);
|
||||
for (int j = 0; j < 3; j++)
|
||||
nrm[j] += fv[j] * multiplier;
|
||||
}
|
||||
Norm3ByMatrix43(normal, nrm, skinMatrix);
|
||||
}
|
||||
|
||||
void VertexDecoder::Step_PosS8() const
|
||||
{
|
||||
float *pos = (float *)(decoded_ + decFmt.posoff);
|
||||
@@ -795,19 +763,43 @@ void VertexDecoder::Step_PosFloatMorph() const
|
||||
}
|
||||
}
|
||||
|
||||
static const StepFunction wtstep[4] = {
|
||||
0,
|
||||
&VertexDecoder::Step_WeightsU8,
|
||||
&VertexDecoder::Step_WeightsU16,
|
||||
&VertexDecoder::Step_WeightsFloat,
|
||||
};
|
||||
void VertexDecoder::Step_PosS8MorphSkin() const {
|
||||
float *v = (float *)(decoded_ + decFmt.posoff);
|
||||
float pos[3]{};
|
||||
for (int n = 0; n < morphcount; n++) {
|
||||
const float multiplier = 1.0f / 128.0f;
|
||||
const s8 *sv = (const s8*)(ptr_ + onesize_ * n + posoff);
|
||||
for (int j = 0; j < 3; j++)
|
||||
pos[j] += (float)sv[j] * (multiplier * gstate_c.morphWeights[n]);
|
||||
}
|
||||
Vec3ByMatrix43(v, pos, skinMatrix);
|
||||
}
|
||||
|
||||
static const StepFunction wtstepToFloat[4] = {
|
||||
0,
|
||||
&VertexDecoder::Step_WeightsU8ToFloat,
|
||||
&VertexDecoder::Step_WeightsU16ToFloat,
|
||||
&VertexDecoder::Step_WeightsFloat,
|
||||
};
|
||||
void VertexDecoder::Step_PosS16MorphSkin() const {
|
||||
float *v = (float *)(decoded_ + decFmt.posoff);
|
||||
float pos[3]{};
|
||||
for (int n = 0; n < morphcount; n++) {
|
||||
const float multiplier = 1.0f / 32768.0f;
|
||||
const s16 *sv = (const s16*)(ptr_ + onesize_ * n + posoff);
|
||||
for (int j = 0; j < 3; j++)
|
||||
pos[j] += (float)sv[j] * (multiplier * gstate_c.morphWeights[n]);
|
||||
}
|
||||
Vec3ByMatrix43(v, pos, skinMatrix);
|
||||
}
|
||||
|
||||
void VertexDecoder::Step_PosFloatMorphSkin() const {
|
||||
float *v = (float *)(decoded_ + decFmt.posoff);
|
||||
float pos[3]{};
|
||||
for (int n = 0; n < morphcount; n++) {
|
||||
const float *fv = (const float*)(ptr_ + onesize_ * n + posoff);
|
||||
for (int j = 0; j < 3; j++)
|
||||
pos[j] += fv[j] * gstate_c.morphWeights[n];
|
||||
}
|
||||
Vec3ByMatrix43(v, pos, skinMatrix);
|
||||
}
|
||||
|
||||
// TODO: Morph weights correctly! This is missing. Not sure if any game actually
|
||||
// use this functionality at all.
|
||||
|
||||
static const StepFunction wtstep_skin[4] = {
|
||||
0,
|
||||
@@ -936,6 +928,13 @@ static const StepFunction nrmstep_morph[4] = {
|
||||
&VertexDecoder::Step_NormalFloatMorph,
|
||||
};
|
||||
|
||||
static const StepFunction nrmstep_morphskin[4] = {
|
||||
0,
|
||||
&VertexDecoder::Step_NormalS8MorphSkin,
|
||||
&VertexDecoder::Step_NormalS16MorphSkin,
|
||||
&VertexDecoder::Step_NormalFloatMorphSkin,
|
||||
};
|
||||
|
||||
static const StepFunction posstep[4] = {
|
||||
&VertexDecoder::Step_PosS8,
|
||||
&VertexDecoder::Step_PosS8,
|
||||
@@ -957,6 +956,13 @@ static const StepFunction posstep_morph[4] = {
|
||||
&VertexDecoder::Step_PosFloatMorph,
|
||||
};
|
||||
|
||||
static const StepFunction posstep_morph_skin[4] = {
|
||||
&VertexDecoder::Step_PosS8MorphSkin,
|
||||
&VertexDecoder::Step_PosS8MorphSkin,
|
||||
&VertexDecoder::Step_PosS16MorphSkin,
|
||||
&VertexDecoder::Step_PosFloatMorphSkin,
|
||||
};
|
||||
|
||||
static const StepFunction posstep_through[4] = {
|
||||
&VertexDecoder::Step_PosS8Through,
|
||||
&VertexDecoder::Step_PosS8Through,
|
||||
@@ -990,7 +996,7 @@ void VertexDecoder::SetVertexType(u32 fmt, const VertexDecoderOptions &options,
|
||||
DEBUG_LOG(G3D, "VTYPE: THRU=%i TC=%i COL=%i POS=%i NRM=%i WT=%i NW=%i IDX=%i MC=%i", (int)throughmode, tc, col, pos, nrm, weighttype, nweights, idx, morphcount);
|
||||
}
|
||||
|
||||
bool skinInDecode = weighttype != 0 && g_Config.bSoftwareSkinning && morphcount == 1;
|
||||
bool skinning = weighttype != 0;
|
||||
|
||||
if (weighttype) { // && nweights?
|
||||
weightoff = size;
|
||||
@@ -999,40 +1005,11 @@ void VertexDecoder::SetVertexType(u32 fmt, const VertexDecoderOptions &options,
|
||||
if (wtalign[weighttype] > biggest)
|
||||
biggest = wtalign[weighttype];
|
||||
|
||||
if (skinInDecode) {
|
||||
steps_[numSteps_++] = wtstep_skin[weighttype];
|
||||
// No visible output, passed in register/external memory to the "pos" step.
|
||||
} else {
|
||||
int fmtBase = DEC_FLOAT_1;
|
||||
if (options.expandAllWeightsToFloat) {
|
||||
steps_[numSteps_++] = wtstepToFloat[weighttype];
|
||||
fmtBase = DEC_FLOAT_1;
|
||||
} else {
|
||||
steps_[numSteps_++] = wtstep[weighttype];
|
||||
if (weighttype == GE_VTYPE_WEIGHT_8BIT >> GE_VTYPE_WEIGHT_SHIFT) {
|
||||
fmtBase = DEC_U8_1;
|
||||
} else if (weighttype == GE_VTYPE_WEIGHT_16BIT >> GE_VTYPE_WEIGHT_SHIFT) {
|
||||
fmtBase = DEC_U16_1;
|
||||
} else if (weighttype == GE_VTYPE_WEIGHT_FLOAT >> GE_VTYPE_WEIGHT_SHIFT) {
|
||||
fmtBase = DEC_FLOAT_1;
|
||||
}
|
||||
}
|
||||
|
||||
int numWeights = TranslateNumBones(nweights);
|
||||
|
||||
if (numWeights <= 4) {
|
||||
decFmt.w0off = decOff;
|
||||
decFmt.w0fmt = fmtBase + numWeights - 1;
|
||||
decOff += DecFmtSize(decFmt.w0fmt);
|
||||
} else {
|
||||
decFmt.w0off = decOff;
|
||||
decFmt.w0fmt = fmtBase + 3;
|
||||
decOff += DecFmtSize(decFmt.w0fmt);
|
||||
decFmt.w1off = decOff;
|
||||
decFmt.w1fmt = fmtBase + numWeights - 5;
|
||||
decOff += DecFmtSize(decFmt.w1fmt);
|
||||
}
|
||||
}
|
||||
// No visible output, computes a matrix that is passed through the skinMatrix variable
|
||||
// to the "nrm" and "pos" steps.
|
||||
// Technically we should support morphing the weights too, but I have a hard time
|
||||
// imagining that any game would use that.. but you never know.
|
||||
steps_[numSteps_++] = wtstep_skin[weighttype];
|
||||
}
|
||||
|
||||
if (tc) {
|
||||
@@ -1090,8 +1067,8 @@ void VertexDecoder::SetVertexType(u32 fmt, const VertexDecoderOptions &options,
|
||||
if (nrmalign[nrm] > biggest)
|
||||
biggest = nrmalign[nrm];
|
||||
|
||||
if (skinInDecode) {
|
||||
steps_[numSteps_++] = nrmstep_skin[nrm];
|
||||
if (skinning) {
|
||||
steps_[numSteps_++] = morphcount == 1 ? nrmstep_skin[nrm] : nrmstep_morphskin[nrm];
|
||||
// After skinning, we always have three floats.
|
||||
decFmt.nrmfmt = DEC_FLOAT_3;
|
||||
} else {
|
||||
@@ -1141,8 +1118,8 @@ void VertexDecoder::SetVertexType(u32 fmt, const VertexDecoderOptions &options,
|
||||
steps_[numSteps_++] = posstep_through[pos];
|
||||
decFmt.posfmt = DEC_FLOAT_3;
|
||||
} else {
|
||||
if (skinInDecode) {
|
||||
steps_[numSteps_++] = posstep_skin[pos];
|
||||
if (skinning) {
|
||||
steps_[numSteps_++] = morphcount == 1 ? posstep_skin[pos] : posstep_morph_skin[pos];
|
||||
decFmt.posfmt = DEC_FLOAT_3;
|
||||
} else {
|
||||
steps_[numSteps_++] = morphcount == 1 ? posstep[pos] : posstep_morph[pos];
|
||||
|
||||
@@ -67,17 +67,16 @@ enum {
|
||||
int DecFmtSize(u8 fmt);
|
||||
|
||||
struct DecVtxFormat {
|
||||
u8 w0fmt; u8 w0off; // first 4 weights
|
||||
u8 w1fmt; u8 w1off; // second 4 weights
|
||||
u8 uvfmt; u8 uvoff;
|
||||
u8 c0fmt; u8 c0off; // First color
|
||||
u8 c1fmt; u8 c1off;
|
||||
u8 nrmfmt; u8 nrmoff;
|
||||
u8 posfmt; u8 posoff;
|
||||
short stride;
|
||||
u8 stride;
|
||||
|
||||
uint32_t id;
|
||||
void ComputeID();
|
||||
void InitializeFromID(uint32_t id);
|
||||
};
|
||||
|
||||
void GetIndexBounds(const void *inds, int count, u32 vertType, u16 *indexLowerBound, u16 *indexUpperBound);
|
||||
@@ -346,61 +345,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void ReadWeights(float weights[8]) const {
|
||||
const float *f = (const float *)(data_ + decFmt_.w0off);
|
||||
const u8 *b = (const u8 *)(data_ + decFmt_.w0off);
|
||||
const u16 *s = (const u16 *)(data_ + decFmt_.w0off);
|
||||
switch (decFmt_.w0fmt) {
|
||||
case DEC_FLOAT_1:
|
||||
case DEC_FLOAT_2:
|
||||
case DEC_FLOAT_3:
|
||||
case DEC_FLOAT_4:
|
||||
for (int i = 0; i <= decFmt_.w0fmt - DEC_FLOAT_1; i++)
|
||||
weights[i] = f[i];
|
||||
break;
|
||||
case DEC_U8_1: weights[0] = b[0] * (1.f / 128.f); break;
|
||||
case DEC_U8_2: for (int i = 0; i < 2; i++) weights[i] = b[i] * (1.f / 128.f); break;
|
||||
case DEC_U8_3: for (int i = 0; i < 3; i++) weights[i] = b[i] * (1.f / 128.f); break;
|
||||
case DEC_U8_4: for (int i = 0; i < 4; i++) weights[i] = b[i] * (1.f / 128.f); break;
|
||||
case DEC_U16_1: weights[0] = s[0] * (1.f / 32768.f); break;
|
||||
case DEC_U16_2: for (int i = 0; i < 2; i++) weights[i] = s[i] * (1.f / 32768.f); break;
|
||||
case DEC_U16_3: for (int i = 0; i < 3; i++) weights[i] = s[i] * (1.f / 32768.f); break;
|
||||
case DEC_U16_4: for (int i = 0; i < 4; i++) weights[i] = s[i] * (1.f / 32768.f); break;
|
||||
default:
|
||||
ERROR_LOG_REPORT_ONCE(fmtw0, G3D, "Reader: Unsupported W0 Format %d", decFmt_.w0fmt);
|
||||
memset(weights, 0, sizeof(float) * 8);
|
||||
break;
|
||||
}
|
||||
|
||||
f = (const float *)(data_ + decFmt_.w1off);
|
||||
b = (const u8 *)(data_ + decFmt_.w1off);
|
||||
s = (const u16 *)(data_ + decFmt_.w1off);
|
||||
switch (decFmt_.w1fmt) {
|
||||
case 0:
|
||||
// It's fine for there to be w0 weights but not w1.
|
||||
break;
|
||||
case DEC_FLOAT_1:
|
||||
case DEC_FLOAT_2:
|
||||
case DEC_FLOAT_3:
|
||||
case DEC_FLOAT_4:
|
||||
for (int i = 0; i <= decFmt_.w1fmt - DEC_FLOAT_1; i++)
|
||||
weights[i+4] = f[i];
|
||||
break;
|
||||
case DEC_U8_1: weights[4] = b[0] * (1.f / 128.f); break;
|
||||
case DEC_U8_2: for (int i = 0; i < 2; i++) weights[i+4] = b[i] * (1.f / 128.f); break;
|
||||
case DEC_U8_3: for (int i = 0; i < 3; i++) weights[i+4] = b[i] * (1.f / 128.f); break;
|
||||
case DEC_U8_4: for (int i = 0; i < 4; i++) weights[i+4] = b[i] * (1.f / 128.f); break;
|
||||
case DEC_U16_1: weights[4] = s[0] * (1.f / 32768.f); break;
|
||||
case DEC_U16_2: for (int i = 0; i < 2; i++) weights[i+4] = s[i] * (1.f / 32768.f); break;
|
||||
case DEC_U16_3: for (int i = 0; i < 3; i++) weights[i+4] = s[i] * (1.f / 32768.f); break;
|
||||
case DEC_U16_4: for (int i = 0; i < 4; i++) weights[i+4] = s[i] * (1.f / 32768.f); break;
|
||||
default:
|
||||
ERROR_LOG_REPORT_ONCE(fmtw1, G3D, "Reader: Unsupported W1 Format %d", decFmt_.w1fmt);
|
||||
memset(weights + 4, 0, sizeof(float) * 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasColor0() const { return decFmt_.c0fmt != 0; }
|
||||
bool hasColor1() const { return decFmt_.c1fmt != 0; }
|
||||
bool hasNormal() const { return decFmt_.nrmfmt != 0; }
|
||||
@@ -431,9 +375,6 @@ struct JitLookup {
|
||||
JitStepFunction jitFunc;
|
||||
};
|
||||
|
||||
// Collapse to less skinning shaders to reduce shader switching, which is expensive.
|
||||
int TranslateNumBones(int bones);
|
||||
|
||||
typedef void(*JittedVertexDecoder)(const u8 *src, u8 *dst, int count);
|
||||
|
||||
struct VertexDecoderOptions {
|
||||
@@ -460,11 +401,7 @@ public:
|
||||
|
||||
std::string GetString(DebugShaderStringType stringType);
|
||||
|
||||
void Step_WeightsU8() const;
|
||||
void Step_WeightsU16() const;
|
||||
void Step_WeightsU8ToFloat() const;
|
||||
void Step_WeightsU16ToFloat() const;
|
||||
void Step_WeightsFloat() const;
|
||||
void ComputeSkinMatrix(const float weights[8]) const;
|
||||
|
||||
void Step_WeightsU8Skin() const;
|
||||
void Step_WeightsU16Skin() const;
|
||||
@@ -517,6 +454,10 @@ public:
|
||||
void Step_NormalS16Morph() const;
|
||||
void Step_NormalFloatMorph() const;
|
||||
|
||||
void Step_NormalS8MorphSkin() const;
|
||||
void Step_NormalS16MorphSkin() const;
|
||||
void Step_NormalFloatMorphSkin() const;
|
||||
|
||||
void Step_PosS8() const;
|
||||
void Step_PosS16() const;
|
||||
void Step_PosFloat() const;
|
||||
@@ -529,6 +470,10 @@ public:
|
||||
void Step_PosS16Morph() const;
|
||||
void Step_PosFloatMorph() const;
|
||||
|
||||
void Step_PosS8MorphSkin() const;
|
||||
void Step_PosS16MorphSkin() const;
|
||||
void Step_PosFloatMorphSkin() const;
|
||||
|
||||
void Step_PosS8Through() const;
|
||||
void Step_PosS16Through() const;
|
||||
void Step_PosFloatThrough() const;
|
||||
@@ -608,12 +553,6 @@ public:
|
||||
JittedVertexDecoder Compile(const VertexDecoder &dec, int32_t *jittedSize);
|
||||
void Clear();
|
||||
|
||||
void Jit_WeightsU8();
|
||||
void Jit_WeightsU16();
|
||||
void Jit_WeightsU8ToFloat();
|
||||
void Jit_WeightsU16ToFloat();
|
||||
void Jit_WeightsFloat();
|
||||
|
||||
void Jit_WeightsU8Skin();
|
||||
void Jit_WeightsU16Skin();
|
||||
void Jit_WeightsFloatSkin();
|
||||
|
||||
+11
-185
@@ -39,6 +39,13 @@ alignas(16) static const float by32768[4] = {
|
||||
1.0f / 32768.0f, 1.0f / 32768.0f, 1.0f / 32768.0f, 1.0f / 32768.0f,
|
||||
};
|
||||
|
||||
alignas(16) static const float by128_11[4] = {
|
||||
1.0f / 128.0f, 1.0f / 128.0f, 1.0f, 1.0f,
|
||||
};
|
||||
alignas(16) static const float by32768_11[4] = {
|
||||
1.0f / 32768.0f, 1.0f / 32768.0f, 1.0f, 1.0f,
|
||||
};
|
||||
|
||||
alignas(16) static const u32 threeMasks[4] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0 };
|
||||
alignas(16) static const u32 aOne[4] = {0, 0, 0, 0x3F800000};
|
||||
|
||||
@@ -87,16 +94,10 @@ static const X64Reg fpScratchReg4 = XMM4;
|
||||
// on the interpreter if the compiler fails.
|
||||
|
||||
static const JitLookup jitLookup[] = {
|
||||
{&VertexDecoder::Step_WeightsU8, &VertexDecoderJitCache::Jit_WeightsU8},
|
||||
{&VertexDecoder::Step_WeightsU16, &VertexDecoderJitCache::Jit_WeightsU16},
|
||||
{&VertexDecoder::Step_WeightsFloat, &VertexDecoderJitCache::Jit_WeightsFloat},
|
||||
{&VertexDecoder::Step_WeightsU8Skin, &VertexDecoderJitCache::Jit_WeightsU8Skin},
|
||||
{&VertexDecoder::Step_WeightsU16Skin, &VertexDecoderJitCache::Jit_WeightsU16Skin},
|
||||
{&VertexDecoder::Step_WeightsFloatSkin, &VertexDecoderJitCache::Jit_WeightsFloatSkin},
|
||||
|
||||
{&VertexDecoder::Step_WeightsU8ToFloat, &VertexDecoderJitCache::Jit_WeightsU8ToFloat},
|
||||
{&VertexDecoder::Step_WeightsU16ToFloat, &VertexDecoderJitCache::Jit_WeightsU16ToFloat},
|
||||
|
||||
{&VertexDecoder::Step_TcFloat, &VertexDecoderJitCache::Jit_TcFloat},
|
||||
{&VertexDecoder::Step_TcU8ToFloat, &VertexDecoderJitCache::Jit_TcU8ToFloat},
|
||||
{&VertexDecoder::Step_TcU16ToFloat, &VertexDecoderJitCache::Jit_TcU16ToFloat},
|
||||
@@ -201,7 +202,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int
|
||||
// Add code to convert matrices to 4x4.
|
||||
// Later we might want to do this when the matrices are loaded instead.
|
||||
int boneCount = 0;
|
||||
if (dec.weighttype && g_Config.bSoftwareSkinning && dec.morphcount == 1) {
|
||||
if (dec.weighttype) {
|
||||
MOV(PTRBITS, R(tempReg1), ImmPtr(&threeMasks));
|
||||
MOVAPS(XMM4, MatR(tempReg1));
|
||||
MOV(PTRBITS, R(tempReg1), ImmPtr(&aOne));
|
||||
@@ -228,20 +229,14 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int
|
||||
// Keep the scale/offset in a few fp registers if we need it.
|
||||
if (prescaleStep) {
|
||||
MOV(PTRBITS, R(tempReg1), ImmPtr(&gstate_c.uv));
|
||||
MOVSS(fpScaleOffsetReg, MDisp(tempReg1, 0));
|
||||
MOVSS(fpScratchReg, MDisp(tempReg1, 4));
|
||||
UNPCKLPS(fpScaleOffsetReg, R(fpScratchReg));
|
||||
MOVUPS(fpScaleOffsetReg, MatR(tempReg1));
|
||||
if ((dec.VertexType() & GE_VTYPE_TC_MASK) == GE_VTYPE_TC_8BIT) {
|
||||
MOV(PTRBITS, R(tempReg2), ImmPtr(&by128));
|
||||
MOV(PTRBITS, R(tempReg2), ImmPtr(&by128_11));
|
||||
MULPS(fpScaleOffsetReg, MatR(tempReg2));
|
||||
} else if ((dec.VertexType() & GE_VTYPE_TC_MASK) == GE_VTYPE_TC_16BIT) {
|
||||
MOV(PTRBITS, R(tempReg2), ImmPtr(&by32768));
|
||||
MOV(PTRBITS, R(tempReg2), ImmPtr(&by32768_11));
|
||||
MULPS(fpScaleOffsetReg, MatR(tempReg2));
|
||||
}
|
||||
MOVSS(fpScratchReg, MDisp(tempReg1, 8));
|
||||
MOVSS(fpScratchReg2, MDisp(tempReg1, 12));
|
||||
UNPCKLPS(fpScratchReg, R(fpScratchReg2));
|
||||
UNPCKLPD(fpScaleOffsetReg, R(fpScratchReg));
|
||||
}
|
||||
|
||||
// Let's not bother with a proper stack frame. We just grab the arguments and go.
|
||||
@@ -281,175 +276,6 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int
|
||||
return (JittedVertexDecoder)start;
|
||||
}
|
||||
|
||||
void VertexDecoderJitCache::Jit_WeightsU8() {
|
||||
switch (dec_->nweights) {
|
||||
case 1:
|
||||
MOVZX(32, 8, tempReg1, MDisp(srcReg, dec_->weightoff));
|
||||
break;
|
||||
case 2:
|
||||
MOVZX(32, 16, tempReg1, MDisp(srcReg, dec_->weightoff));
|
||||
break;
|
||||
case 3:
|
||||
MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff));
|
||||
AND(32, R(tempReg1), Imm32(0x00FFFFFF));
|
||||
break;
|
||||
case 4:
|
||||
MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff));
|
||||
break;
|
||||
case 5:
|
||||
MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff));
|
||||
MOVZX(32, 8, tempReg2, MDisp(srcReg, dec_->weightoff + 4));
|
||||
break;
|
||||
case 6:
|
||||
MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff));
|
||||
MOVZX(32, 16, tempReg2, MDisp(srcReg, dec_->weightoff + 4));
|
||||
break;
|
||||
case 7:
|
||||
MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff));
|
||||
MOV(32, R(tempReg2), MDisp(srcReg, dec_->weightoff + 4));
|
||||
AND(32, R(tempReg2), Imm32(0x00FFFFFF));
|
||||
break;
|
||||
case 8:
|
||||
MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff));
|
||||
MOV(32, R(tempReg2), MDisp(srcReg, dec_->weightoff + 4));
|
||||
break;
|
||||
}
|
||||
|
||||
if (dec_->nweights <= 4) {
|
||||
MOV(32, MDisp(dstReg, dec_->decFmt.w0off), R(tempReg1));
|
||||
} else {
|
||||
MOV(32, MDisp(dstReg, dec_->decFmt.w0off), R(tempReg1));
|
||||
MOV(32, MDisp(dstReg, dec_->decFmt.w1off), R(tempReg2));
|
||||
}
|
||||
}
|
||||
|
||||
void VertexDecoderJitCache::Jit_WeightsU16() {
|
||||
switch (dec_->nweights) {
|
||||
case 1:
|
||||
MOVZX(32, 16, tempReg1, MDisp(srcReg, dec_->weightoff));
|
||||
MOV(32, MDisp(dstReg, dec_->decFmt.w0off), R(tempReg1));
|
||||
MOV(32, MDisp(dstReg, dec_->decFmt.w0off + 4), Imm32(0));
|
||||
return;
|
||||
|
||||
case 2:
|
||||
MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff));
|
||||
MOV(32, MDisp(dstReg, dec_->decFmt.w0off), R(tempReg1));
|
||||
MOV(32, MDisp(dstReg, dec_->decFmt.w0off + 4), Imm32(0));
|
||||
return;
|
||||
|
||||
case 3:
|
||||
MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff));
|
||||
MOVZX(32, 16, tempReg2, MDisp(srcReg, dec_->weightoff + 4));
|
||||
MOV(32, MDisp(dstReg, dec_->decFmt.w0off), R(tempReg1));
|
||||
MOV(32, MDisp(dstReg, dec_->decFmt.w0off + 4), R(tempReg2));
|
||||
return;
|
||||
|
||||
case 4:
|
||||
// Anything above 4 will do 4 here, and then the rest after.
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff));
|
||||
MOV(32, R(tempReg2), MDisp(srcReg, dec_->weightoff + 4));
|
||||
MOV(32, MDisp(dstReg, dec_->decFmt.w0off), R(tempReg1));
|
||||
MOV(32, MDisp(dstReg, dec_->decFmt.w0off + 4), R(tempReg2));
|
||||
break;
|
||||
}
|
||||
|
||||
// Basic implementation - a short at a time. TODO: Optimize
|
||||
int j;
|
||||
for (j = 4; j < dec_->nweights; j++) {
|
||||
MOV(16, R(tempReg1), MDisp(srcReg, dec_->weightoff + j * 2));
|
||||
MOV(16, MDisp(dstReg, dec_->decFmt.w0off + j * 2), R(tempReg1));
|
||||
}
|
||||
while (j & 3) {
|
||||
MOV(16, MDisp(dstReg, dec_->decFmt.w0off + j * 2), Imm16(0));
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
void VertexDecoderJitCache::Jit_WeightsU8ToFloat() {
|
||||
if (dec_->nweights >= 4) {
|
||||
Jit_AnyU8ToFloat(dec_->weightoff, 32);
|
||||
MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3);
|
||||
if (dec_->nweights > 4) {
|
||||
Jit_AnyU8ToFloat(dec_->weightoff + 4, (dec_->nweights - 4) * 8);
|
||||
MOVUPS(MDisp(dstReg, dec_->decFmt.w1off), XMM3);
|
||||
}
|
||||
} else {
|
||||
Jit_AnyU8ToFloat(dec_->weightoff, dec_->nweights * 8);
|
||||
MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3);
|
||||
}
|
||||
}
|
||||
|
||||
void VertexDecoderJitCache::Jit_WeightsU16ToFloat() {
|
||||
if (dec_->nweights >= 4) {
|
||||
Jit_AnyU16ToFloat(dec_->weightoff, 64);
|
||||
MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3);
|
||||
if (dec_->nweights > 4) {
|
||||
Jit_AnyU16ToFloat(dec_->weightoff + 4 * 2, (dec_->nweights - 4) * 16);
|
||||
MOVUPS(MDisp(dstReg, dec_->decFmt.w1off), XMM3);
|
||||
}
|
||||
} else {
|
||||
Jit_AnyU16ToFloat(dec_->weightoff, dec_->nweights * 16);
|
||||
MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3);
|
||||
}
|
||||
}
|
||||
|
||||
void VertexDecoderJitCache::Jit_WeightsFloat() {
|
||||
int j;
|
||||
switch (dec_->nweights) {
|
||||
case 1:
|
||||
// MOVSS: When the source operand is a memory location and destination operand is an XMM register, the three high-order doublewords of the destination operand are cleared to all 0s.
|
||||
MOVSS(XMM3, MDisp(srcReg, dec_->weightoff));
|
||||
MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
MOVQ_xmm(XMM3, MDisp(srcReg, dec_->weightoff));
|
||||
MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
MOVUPS(XMM3, MDisp(srcReg, dec_->weightoff));
|
||||
MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
MOVUPS(XMM3, MDisp(srcReg, dec_->weightoff));
|
||||
MOVSS(XMM4, MDisp(srcReg, dec_->weightoff + 16));
|
||||
MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3);
|
||||
MOVUPS(MDisp(dstReg, dec_->decFmt.w0off + 16), XMM4);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
MOVUPS(XMM3, MDisp(srcReg, dec_->weightoff));
|
||||
MOVQ_xmm(XMM4, MDisp(srcReg, dec_->weightoff + 16));
|
||||
MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3);
|
||||
MOVUPS(MDisp(dstReg, dec_->decFmt.w0off + 16), XMM4);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
MOVUPS(XMM3, MDisp(srcReg, dec_->weightoff));
|
||||
MOVUPS(XMM4, MDisp(srcReg, dec_->weightoff + 16));
|
||||
MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3);
|
||||
MOVUPS(MDisp(dstReg, dec_->decFmt.w0off + 16), XMM4);
|
||||
break;
|
||||
|
||||
default:
|
||||
for (j = 0; j < dec_->nweights; j++) {
|
||||
MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff + j * 4));
|
||||
MOV(32, MDisp(dstReg, dec_->decFmt.w0off + j * 4), R(tempReg1));
|
||||
}
|
||||
while (j & 3) { // Zero additional weights rounding up to 4.
|
||||
MOV(32, MDisp(dstReg, dec_->decFmt.w0off + j * 4), Imm32(0));
|
||||
j++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void VertexDecoderJitCache::Jit_WeightsU8Skin() {
|
||||
MOV(PTRBITS, R(tempReg2), ImmPtr(&bones));
|
||||
|
||||
|
||||
@@ -204,18 +204,6 @@ ID3D11InputLayout *DrawEngineD3D11::SetupDecFmtForDraw(D3D11VertexShader *vshade
|
||||
D3D11_INPUT_ELEMENT_DESC VertexElements[8];
|
||||
D3D11_INPUT_ELEMENT_DESC *VertexElement = &VertexElements[0];
|
||||
|
||||
// Vertices Elements orders
|
||||
// WEIGHT
|
||||
if (decFmt.w0fmt != 0) {
|
||||
VertexAttribSetup(VertexElement, decFmt.w0fmt, decFmt.w0off, "TEXCOORD", 1);
|
||||
VertexElement++;
|
||||
}
|
||||
|
||||
if (decFmt.w1fmt != 0) {
|
||||
VertexAttribSetup(VertexElement, decFmt.w1fmt, decFmt.w1off, "TEXCOORD", 2);
|
||||
VertexElement++;
|
||||
}
|
||||
|
||||
// TC
|
||||
if (decFmt.uvfmt != 0) {
|
||||
VertexAttribSetup(VertexElement, decFmt.uvfmt, decFmt.uvoff, "TEXCOORD", 0);
|
||||
@@ -257,72 +245,6 @@ ID3D11InputLayout *DrawEngineD3D11::SetupDecFmtForDraw(D3D11VertexShader *vshade
|
||||
}
|
||||
}
|
||||
|
||||
void DrawEngineD3D11::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) {
|
||||
if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls >= MAX_DEFERRED_DRAW_CALLS || vertexCountInDrawCalls_ + vertexCount > VERTEX_BUFFER_MAX)
|
||||
Flush();
|
||||
|
||||
// TODO: Is this the right thing to do?
|
||||
if (prim == GE_PRIM_KEEP_PREVIOUS) {
|
||||
prim = prevPrim_ != GE_PRIM_INVALID ? prevPrim_ : GE_PRIM_POINTS;
|
||||
} else {
|
||||
prevPrim_ = prim;
|
||||
}
|
||||
|
||||
SetupVertexDecoder(vertType);
|
||||
|
||||
*bytesRead = vertexCount * dec_->VertexSize();
|
||||
|
||||
if ((vertexCount < 2 && prim > 0) || (vertexCount < 3 && prim > 2 && prim != GE_PRIM_RECTANGLES))
|
||||
return;
|
||||
|
||||
DeferredDrawCall &dc = drawCalls[numDrawCalls];
|
||||
dc.verts = verts;
|
||||
dc.inds = inds;
|
||||
dc.vertType = vertType;
|
||||
dc.indexType = (vertType & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT;
|
||||
dc.prim = prim;
|
||||
dc.vertexCount = vertexCount;
|
||||
|
||||
if (g_Config.bVertexCache) {
|
||||
u32 dhash = dcid_;
|
||||
dhash ^= (u32)(uintptr_t)verts;
|
||||
dhash = __rotl(dhash, 13);
|
||||
dhash ^= (u32)(uintptr_t)inds;
|
||||
dhash = __rotl(dhash, 13);
|
||||
dhash ^= (u32)vertType;
|
||||
dhash = __rotl(dhash, 13);
|
||||
dhash ^= (u32)vertexCount;
|
||||
dhash = __rotl(dhash, 13);
|
||||
dhash ^= (u32)prim;
|
||||
dcid_ = dhash;
|
||||
}
|
||||
|
||||
if (inds) {
|
||||
GetIndexBounds(inds, vertexCount, vertType, &dc.indexLowerBound, &dc.indexUpperBound);
|
||||
} else {
|
||||
dc.indexLowerBound = 0;
|
||||
dc.indexUpperBound = vertexCount - 1;
|
||||
}
|
||||
|
||||
uvScale[numDrawCalls] = gstate_c.uv;
|
||||
|
||||
numDrawCalls++;
|
||||
vertexCountInDrawCalls_ += vertexCount;
|
||||
|
||||
if (g_Config.bSoftwareSkinning && (vertType & GE_VTYPE_WEIGHT_MASK)) {
|
||||
DecodeVertsStep(decoded, decodeCounter_, decodedVerts_);
|
||||
decodeCounter_++;
|
||||
}
|
||||
|
||||
if (prim == GE_PRIM_RECTANGLES && (gstate.getTextureAddress(0) & 0x3FFFFFFF) == (gstate.getFrameBufAddress() & 0x3FFFFFFF)) {
|
||||
// Rendertarget == texture?
|
||||
if (!g_Config.bDisableSlowFramebufEffects) {
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawEngineD3D11::MarkUnreliable(VertexArrayInfoD3D11 *vai) {
|
||||
vai->status = VertexArrayInfoD3D11::VAI_UNRELIABLE;
|
||||
if (vai->vbo) {
|
||||
@@ -409,8 +331,8 @@ void DrawEngineD3D11::DoFlush() {
|
||||
|
||||
// Cannot cache vertex data with morph enabled.
|
||||
bool useCache = g_Config.bVertexCache && !(lastVType_ & GE_VTYPE_MORPHCOUNT_MASK);
|
||||
// Also avoid caching when software skinning.
|
||||
if (g_Config.bSoftwareSkinning && (lastVType_ & GE_VTYPE_WEIGHT_MASK))
|
||||
// Also avoid caching when skinning.
|
||||
if (lastVType_ & GE_VTYPE_WEIGHT_MASK)
|
||||
useCache = false;
|
||||
|
||||
if (useCache) {
|
||||
|
||||
@@ -105,8 +105,6 @@ public:
|
||||
DrawEngineD3D11(Draw::DrawContext *draw, ID3D11Device *device, ID3D11DeviceContext *context);
|
||||
virtual ~DrawEngineD3D11();
|
||||
|
||||
void SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead);
|
||||
|
||||
void SetShaderManager(ShaderManagerD3D11 *shaderManager) {
|
||||
shaderManager_ = shaderManager;
|
||||
}
|
||||
@@ -135,9 +133,6 @@ public:
|
||||
}
|
||||
|
||||
void DispatchFlush() override { Flush(); }
|
||||
void DispatchSubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) override {
|
||||
SubmitPrim(verts, inds, prim, vertexCount, vertType, bytesRead);
|
||||
}
|
||||
|
||||
void ClearTrackedVertexArrays() override;
|
||||
|
||||
|
||||
@@ -197,13 +197,6 @@ void FramebufferManagerD3D11::SetDrawEngine(DrawEngineD3D11 *td) {
|
||||
drawEngine_ = td;
|
||||
}
|
||||
|
||||
void FramebufferManagerD3D11::DisableState() {
|
||||
context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0xF], nullptr, 0xFFFFFFFF);
|
||||
context_->RSSetState(stockD3D11.rasterStateNoCull);
|
||||
context_->OMSetDepthStencilState(stockD3D11.depthStencilDisabled, 0xFF);
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE);
|
||||
}
|
||||
|
||||
void FramebufferManagerD3D11::CompilePostShader() {
|
||||
std::string vsSource;
|
||||
std::string psSource;
|
||||
@@ -459,15 +452,6 @@ void FramebufferManagerD3D11::BindPostShader(const PostShaderUniforms &uniforms)
|
||||
context_->PSSetConstantBuffers(0, 1, &postConstants_);
|
||||
}
|
||||
|
||||
void FramebufferManagerD3D11::RebindFramebuffer() {
|
||||
if (currentRenderVfb_ && currentRenderVfb_->fbo) {
|
||||
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
} else {
|
||||
// Should this even happen?
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferManagerD3D11::ReformatFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat old) {
|
||||
if (!useBufferedRendering_ || !vfb->fbo) {
|
||||
return;
|
||||
@@ -740,8 +724,6 @@ void FramebufferManagerD3D11::DestroyAllFBOs() {
|
||||
tempFBOs_.clear();
|
||||
|
||||
SetNumExtraFBOs(0);
|
||||
|
||||
DisableState();
|
||||
}
|
||||
|
||||
void FramebufferManagerD3D11::Resized() {
|
||||
|
||||
@@ -59,16 +59,12 @@ public:
|
||||
|
||||
virtual bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override;
|
||||
|
||||
virtual void RebindFramebuffer() override;
|
||||
|
||||
// TODO: Remove
|
||||
ID3D11Buffer *GetDynamicQuadBuffer() {
|
||||
return quadBuffer_;
|
||||
}
|
||||
|
||||
protected:
|
||||
void DisableState() override;
|
||||
|
||||
// Used by ReadFramebufferToMemory and later framebuffer block copies
|
||||
void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) override;
|
||||
|
||||
|
||||
+6
-217
@@ -66,31 +66,6 @@
|
||||
#include "Core/HLE/sceKernelInterrupt.h"
|
||||
#include "Core/HLE/sceGe.h"
|
||||
|
||||
struct D3D11CommandTableEntry {
|
||||
uint8_t cmd;
|
||||
uint8_t flags;
|
||||
uint64_t dirty;
|
||||
GPU_D3D11::CmdFunc func;
|
||||
};
|
||||
|
||||
// This table gets crunched into a faster form by init.
|
||||
static const D3D11CommandTableEntry commandTable[] = {
|
||||
// Changes that dirty the current texture.
|
||||
{ GE_CMD_TEXSIZE0, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPUCommon::Execute_TexSize0 },
|
||||
|
||||
// Changing the vertex type requires us to flush.
|
||||
{ GE_CMD_VERTEXTYPE, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_VertexType },
|
||||
|
||||
{ GE_CMD_PRIM, FLAG_EXECUTE, 0, &GPU_D3D11::Execute_Prim },
|
||||
{ GE_CMD_BEZIER, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPUCommon::Execute_Bezier },
|
||||
{ GE_CMD_SPLINE, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPUCommon::Execute_Spline },
|
||||
|
||||
// Changes that trigger data copies. Only flushing on change for LOADCLUT must be a bit of a hack...
|
||||
{ GE_CMD_LOADCLUT, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPU_D3D11::Execute_LoadClut },
|
||||
};
|
||||
|
||||
GPU_D3D11::CommandInfo GPU_D3D11::cmdInfo_[256]{};
|
||||
|
||||
GPU_D3D11::GPU_D3D11(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
||||
: GPUCommon(gfxCtx, draw), drawEngine_(draw,
|
||||
(ID3D11Device *)draw->GetNativeObject(Draw::NativeObject::DEVICE),
|
||||
@@ -126,48 +101,9 @@ GPU_D3D11::GPU_D3D11(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
||||
ERROR_LOG(G3D, "gstate has drifted out of sync!");
|
||||
}
|
||||
|
||||
memset(cmdInfo_, 0, sizeof(cmdInfo_));
|
||||
|
||||
// Import both the global and local command tables, and check for dupes
|
||||
std::set<u8> dupeCheck;
|
||||
for (size_t i = 0; i < commonCommandTableSize; i++) {
|
||||
const u8 cmd = commonCommandTable[i].cmd;
|
||||
if (dupeCheck.find(cmd) != dupeCheck.end()) {
|
||||
ERROR_LOG(G3D, "Command table Dupe: %02x (%i)", (int)cmd, (int)cmd);
|
||||
} else {
|
||||
dupeCheck.insert(cmd);
|
||||
}
|
||||
cmdInfo_[cmd].flags |= (uint64_t)commonCommandTable[i].flags | (commonCommandTable[i].dirty << 8);
|
||||
cmdInfo_[cmd].func = commonCommandTable[i].func;
|
||||
if ((cmdInfo_[cmd].flags & (FLAG_EXECUTE | FLAG_EXECUTEONCHANGE)) && !cmdInfo_[cmd].func) {
|
||||
Crash();
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(commandTable); i++) {
|
||||
const u8 cmd = commandTable[i].cmd;
|
||||
if (dupeCheck.find(cmd) != dupeCheck.end()) {
|
||||
ERROR_LOG(G3D, "Command table Dupe: %02x (%i)", (int)cmd, (int)cmd);
|
||||
} else {
|
||||
dupeCheck.insert(cmd);
|
||||
}
|
||||
cmdInfo_[cmd].flags |= (uint64_t)commandTable[i].flags | (commandTable[i].dirty << 8);
|
||||
cmdInfo_[cmd].func = commandTable[i].func;
|
||||
if ((cmdInfo_[cmd].flags & (FLAG_EXECUTE | FLAG_EXECUTEONCHANGE)) && !cmdInfo_[cmd].func) {
|
||||
Crash();
|
||||
}
|
||||
}
|
||||
|
||||
// Find commands missing from the table.
|
||||
for (int i = 0; i < 0xEF; i++) {
|
||||
if (dupeCheck.find((u8)i) == dupeCheck.end()) {
|
||||
ERROR_LOG(G3D, "Command missing from table: %02x (%i)", i, i);
|
||||
}
|
||||
}
|
||||
|
||||
// No need to flush before the tex scale/offset commands if we are baking
|
||||
// the tex scale/offset into the vertices anyway.
|
||||
UpdateCmdInfo();
|
||||
CheckGPUFeatures();
|
||||
|
||||
BuildReportingInfo();
|
||||
|
||||
@@ -187,26 +123,16 @@ GPU_D3D11::~GPU_D3D11() {
|
||||
stockD3D11.Destroy();
|
||||
}
|
||||
|
||||
void GPU_D3D11::UpdateCmdInfo() {
|
||||
if (g_Config.bSoftwareSkinning) {
|
||||
cmdInfo_[GE_CMD_VERTEXTYPE].flags &= ~FLAG_FLUSHBEFOREONCHANGE;
|
||||
cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommon::Execute_VertexTypeSkinning;
|
||||
} else {
|
||||
cmdInfo_[GE_CMD_VERTEXTYPE].flags |= FLAG_FLUSHBEFOREONCHANGE;
|
||||
cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommon::Execute_VertexType;
|
||||
}
|
||||
|
||||
CheckGPUFeatures();
|
||||
}
|
||||
|
||||
void GPU_D3D11::CheckGPUFeatures() {
|
||||
u32 features = 0;
|
||||
|
||||
features |= GPU_SUPPORTS_BLEND_MINMAX;
|
||||
features |= GPU_PREFER_CPU_DOWNLOAD;
|
||||
|
||||
// Accurate depth is required on AMD so we ignore the compat flag to disable it on those. See #9545
|
||||
if (!PSP_CoreParameter().compat.flags().DisableAccurateDepth || draw_->GetDeviceCaps().vendor == Draw::GPUVendor::VENDOR_AMD) {
|
||||
// Accurate depth is required on AMD/nVidia (for reverse Z) so we ignore the compat flag to disable it on those. See #9545
|
||||
auto vendor = draw_->GetDeviceCaps().vendor;
|
||||
|
||||
if (!PSP_CoreParameter().compat.flags().DisableAccurateDepth || vendor == Draw::GPUVendor::VENDOR_AMD || vendor == Draw::GPUVendor::VENDOR_NVIDIA) {
|
||||
features |= GPU_SUPPORTS_ACCURATE_DEPTH; // Breaks text in PaRappa for some reason.
|
||||
}
|
||||
|
||||
@@ -288,8 +214,8 @@ void GPU_D3D11::InitClear() {
|
||||
|
||||
void GPU_D3D11::BeginHostFrame() {
|
||||
GPUCommon::BeginHostFrame();
|
||||
UpdateCmdInfo();
|
||||
if (resized_) {
|
||||
CheckGPUFeatures();
|
||||
framebufferManager_->Resized();
|
||||
drawEngine_.Resized();
|
||||
textureCacheD3D11_->NotifyConfigChanged();
|
||||
@@ -330,26 +256,6 @@ void GPU_D3D11::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat f
|
||||
framebufferManagerD3D11_->SetDisplayFramebuffer(framebuf, stride, format);
|
||||
}
|
||||
|
||||
bool GPU_D3D11::FramebufferDirty() {
|
||||
VirtualFramebuffer *vfb = framebufferManager_->GetDisplayVFB();
|
||||
if (vfb) {
|
||||
bool dirty = vfb->dirtyAfterDisplay;
|
||||
vfb->dirtyAfterDisplay = false;
|
||||
return dirty;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU_D3D11::FramebufferReallyDirty() {
|
||||
VirtualFramebuffer *vfb = framebufferManager_->GetDisplayVFB();
|
||||
if (vfb) {
|
||||
bool dirty = vfb->reallyDirtyAfterDisplay;
|
||||
vfb->reallyDirtyAfterDisplay = false;
|
||||
return dirty;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPU_D3D11::CopyDisplayToOutput() {
|
||||
float blendColor[4]{};
|
||||
context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0xF], blendColor, 0xFFFFFFFF);
|
||||
@@ -365,44 +271,6 @@ void GPU_D3D11::CopyDisplayToOutput() {
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_IMAGE);
|
||||
}
|
||||
|
||||
// Maybe should write this in ASM...
|
||||
void GPU_D3D11::FastRunLoop(DisplayList &list) {
|
||||
PROFILE_THIS_SCOPE("gpuloop");
|
||||
const CommandInfo *cmdInfo = cmdInfo_;
|
||||
int dc = downcount;
|
||||
for (; dc > 0; --dc) {
|
||||
// We know that display list PCs have the upper nibble == 0 - no need to mask the pointer
|
||||
const u32 op = *(const u32 *)(Memory::base + list.pc);
|
||||
const u32 cmd = op >> 24;
|
||||
const CommandInfo &info = cmdInfo[cmd];
|
||||
const u32 diff = op ^ gstate.cmdmem[cmd];
|
||||
if (diff == 0) {
|
||||
if (info.flags & FLAG_EXECUTE) {
|
||||
downcount = dc;
|
||||
(this->*info.func)(op, diff);
|
||||
dc = downcount;
|
||||
}
|
||||
} else {
|
||||
uint64_t flags = info.flags;
|
||||
if (flags & FLAG_FLUSHBEFOREONCHANGE) {
|
||||
drawEngine_.Flush();
|
||||
}
|
||||
gstate.cmdmem[cmd] = op;
|
||||
if (flags & (FLAG_EXECUTE | FLAG_EXECUTEONCHANGE)) {
|
||||
downcount = dc;
|
||||
(this->*info.func)(op, diff);
|
||||
dc = downcount;
|
||||
} else {
|
||||
uint64_t dirty = flags >> 8;
|
||||
if (dirty)
|
||||
gstate_c.Dirty(dirty);
|
||||
}
|
||||
}
|
||||
list.pc += 4;
|
||||
}
|
||||
downcount = 0;
|
||||
}
|
||||
|
||||
void GPU_D3D11::FinishDeferred() {
|
||||
// This finishes reading any vertex data that is pending.
|
||||
drawEngine_.FinishDeferred();
|
||||
@@ -435,85 +303,6 @@ void GPU_D3D11::ExecuteOp(u32 op, u32 diff) {
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_D3D11::Execute_Prim(u32 op, u32 diff) {
|
||||
// This drives all drawing. All other state we just buffer up, then we apply it only
|
||||
// when it's time to draw. As most PSP games set state redundantly ALL THE TIME, this is a huge optimization.
|
||||
|
||||
u32 data = op & 0xFFFFFF;
|
||||
u32 count = data & 0xFFFF;
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
// Upper bits are ignored.
|
||||
GEPrimitiveType prim = static_cast<GEPrimitiveType>((data >> 16) & 7);
|
||||
SetDrawType(DRAW_PRIM, prim);
|
||||
|
||||
// Discard AA lines as we can't do anything that makes sense with these anyway. The SW plugin might, though.
|
||||
|
||||
if (gstate.isAntiAliasEnabled()) {
|
||||
// Discard AA lines in DOA
|
||||
if (prim == GE_PRIM_LINE_STRIP)
|
||||
return;
|
||||
// Discard AA lines in Summon Night 5
|
||||
if ((prim == GE_PRIM_LINES) && gstate.isSkinningEnabled())
|
||||
return;
|
||||
}
|
||||
|
||||
// This also make skipping drawing very effective.
|
||||
framebufferManagerD3D11_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason);
|
||||
if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) {
|
||||
drawEngine_.SetupVertexDecoder(gstate.vertType);
|
||||
// Rough estimate, not sure what's correct.
|
||||
cyclesExecuted += EstimatePerVertexCost() * count;
|
||||
return;
|
||||
}
|
||||
|
||||
u32 vertexAddr = gstate_c.vertexAddr;
|
||||
if (!Memory::IsValidAddress(vertexAddr)) {
|
||||
ERROR_LOG_REPORT(G3D, "Bad vertex address %08x!", vertexAddr);
|
||||
return;
|
||||
}
|
||||
|
||||
void *verts = Memory::GetPointerUnchecked(vertexAddr);
|
||||
void *inds = 0;
|
||||
u32 vertexType = gstate.vertType;
|
||||
if ((vertexType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
|
||||
u32 indexAddr = gstate_c.indexAddr;
|
||||
if (!Memory::IsValidAddress(indexAddr)) {
|
||||
ERROR_LOG_REPORT(G3D, "Bad index address %08x!", indexAddr);
|
||||
return;
|
||||
}
|
||||
inds = Memory::GetPointerUnchecked(indexAddr);
|
||||
}
|
||||
|
||||
#ifndef MOBILE_DEVICE
|
||||
if (prim > GE_PRIM_RECTANGLES) {
|
||||
ERROR_LOG_REPORT_ONCE(reportPrim, G3D, "Unexpected prim type: %d", prim);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gstate_c.dirty & DIRTY_VERTEXSHADER_STATE) {
|
||||
vertexCost_ = EstimatePerVertexCost();
|
||||
}
|
||||
gpuStats.vertexGPUCycles += vertexCost_ * count;
|
||||
cyclesExecuted += vertexCost_* count;
|
||||
|
||||
int bytesRead = 0;
|
||||
UpdateUVScaleOffset();
|
||||
drawEngine_.SubmitPrim(verts, inds, prim, count, vertexType, &bytesRead);
|
||||
|
||||
// After drawing, we advance the vertexAddr (when non indexed) or indexAddr (when indexed).
|
||||
// Some games rely on this, they don't bother reloading VADDR and IADDR.
|
||||
// The VADDR/IADDR registers are NOT updated.
|
||||
AdvanceVerts(vertexType, count, bytesRead);
|
||||
}
|
||||
|
||||
void GPU_D3D11::Execute_LoadClut(u32 op, u32 diff) {
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);
|
||||
textureCacheD3D11_->LoadClut(gstate.getClutAddress(), gstate.getClutLoadBytes());
|
||||
// This could be used to "dirty" textures with clut.
|
||||
}
|
||||
|
||||
void GPU_D3D11::GetStats(char *buffer, size_t bufsize) {
|
||||
float vertexAverageCycles = gpuStats.numVertsSubmitted > 0 ? (float)gpuStats.vertexGPUCycles / (float)gpuStats.numVertsSubmitted : 0.0f;
|
||||
snprintf(buffer, bufsize - 1,
|
||||
|
||||
+1
-28
@@ -36,7 +36,7 @@ public:
|
||||
GPU_D3D11(GraphicsContext *gfxCtx, Draw::DrawContext *draw);
|
||||
~GPU_D3D11();
|
||||
|
||||
void CheckGPUFeatures();
|
||||
void CheckGPUFeatures() override;
|
||||
void PreExecuteOp(u32 op, u32 diff) override;
|
||||
void ExecuteOp(u32 op, u32 diff) override;
|
||||
|
||||
@@ -50,25 +50,6 @@ public:
|
||||
void DoState(PointerWrap &p) override;
|
||||
|
||||
void ClearShaderCache() override;
|
||||
bool DecodeTexture(u8 *dest, const GPUgstate &state) override {
|
||||
return false;
|
||||
}
|
||||
bool FramebufferDirty() override;
|
||||
bool FramebufferReallyDirty() override;
|
||||
|
||||
void GetReportingInfo(std::string &primaryInfo, std::string &fullInfo) override {
|
||||
primaryInfo = reportingPrimaryInfo_;
|
||||
fullInfo = reportingFullInfo_;
|
||||
}
|
||||
|
||||
typedef void (GPU_D3D11::*CmdFunc)(u32 op, u32 diff);
|
||||
struct CommandInfo {
|
||||
uint64_t flags;
|
||||
GPU_D3D11::CmdFunc func;
|
||||
};
|
||||
|
||||
void Execute_Prim(u32 op, u32 diff);
|
||||
void Execute_LoadClut(u32 op, u32 diff);
|
||||
|
||||
// Using string because it's generic - makes no assumptions on the size of the shader IDs of this backend.
|
||||
std::vector<std::string> DebugGetShaderIDs(DebugShaderType shader) override;
|
||||
@@ -78,12 +59,9 @@ public:
|
||||
void EndHostFrame() override;
|
||||
|
||||
protected:
|
||||
void FastRunLoop(DisplayList &list) override;
|
||||
void FinishDeferred() override;
|
||||
|
||||
private:
|
||||
void UpdateCmdInfo();
|
||||
|
||||
void Flush() {
|
||||
drawEngine_.Flush();
|
||||
}
|
||||
@@ -104,11 +82,6 @@ private:
|
||||
DrawEngineD3D11 drawEngine_;
|
||||
ShaderManagerD3D11 *shaderManagerD3D11_;
|
||||
|
||||
static CommandInfo cmdInfo_[256];
|
||||
|
||||
int lastVsync_;
|
||||
int vertexCost_ = 0;
|
||||
|
||||
std::string reportingPrimaryInfo_;
|
||||
std::string reportingFullInfo_;
|
||||
};
|
||||
@@ -63,8 +63,8 @@ std::string D3D11FragmentShader::GetShaderString(DebugShaderStringType type) con
|
||||
}
|
||||
}
|
||||
|
||||
D3D11VertexShader::D3D11VertexShader(ID3D11Device *device, D3D_FEATURE_LEVEL featureLevel, VShaderID id, const char *code, int vertType, bool useHWTransform, bool usesLighting)
|
||||
: device_(device), id_(id), failed_(false), useHWTransform_(useHWTransform), module_(nullptr), usesLighting_(usesLighting) {
|
||||
D3D11VertexShader::D3D11VertexShader(ID3D11Device *device, D3D_FEATURE_LEVEL featureLevel, VShaderID id, const char *code, int vertType, bool useHWTransform)
|
||||
: device_(device), id_(id), failed_(false), useHWTransform_(useHWTransform), module_(nullptr) {
|
||||
source_ = code;
|
||||
|
||||
module_ = CreateVertexShaderD3D11(device, code, strlen(code), &bytecode_, featureLevel);
|
||||
@@ -93,24 +93,19 @@ ShaderManagerD3D11::ShaderManagerD3D11(ID3D11Device *device, ID3D11DeviceContext
|
||||
codeBuffer_ = new char[16384];
|
||||
memset(&ub_base, 0, sizeof(ub_base));
|
||||
memset(&ub_lights, 0, sizeof(ub_lights));
|
||||
memset(&ub_bones, 0, sizeof(ub_bones));
|
||||
|
||||
INFO_LOG(G3D, "sizeof(ub_base): %d", (int)sizeof(ub_base));
|
||||
INFO_LOG(G3D, "sizeof(ub_lights): %d", (int)sizeof(ub_lights));
|
||||
INFO_LOG(G3D, "sizeof(ub_bones): %d", (int)sizeof(ub_bones));
|
||||
|
||||
D3D11_BUFFER_DESC desc{sizeof(ub_base), D3D11_USAGE_DYNAMIC, D3D11_BIND_CONSTANT_BUFFER, D3D11_CPU_ACCESS_WRITE };
|
||||
ASSERT_SUCCESS(device_->CreateBuffer(&desc, nullptr, &push_base));
|
||||
desc.ByteWidth = sizeof(ub_lights);
|
||||
ASSERT_SUCCESS(device_->CreateBuffer(&desc, nullptr, &push_lights));
|
||||
desc.ByteWidth = sizeof(ub_bones);
|
||||
ASSERT_SUCCESS(device_->CreateBuffer(&desc, nullptr, &push_bones));
|
||||
}
|
||||
|
||||
ShaderManagerD3D11::~ShaderManagerD3D11() {
|
||||
push_base->Release();
|
||||
push_lights->Release();
|
||||
push_bones->Release();
|
||||
ClearShaders();
|
||||
delete[] codeBuffer_;
|
||||
}
|
||||
@@ -159,21 +154,15 @@ uint64_t ShaderManagerD3D11::UpdateUniforms() {
|
||||
memcpy(map.pData, &ub_lights, sizeof(ub_lights));
|
||||
context_->Unmap(push_lights, 0);
|
||||
}
|
||||
if (dirty & DIRTY_BONE_UNIFORMS) {
|
||||
BoneUpdateUniforms(&ub_bones, dirty);
|
||||
context_->Map(push_bones, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
|
||||
memcpy(map.pData, &ub_bones, sizeof(ub_bones));
|
||||
context_->Unmap(push_bones, 0);
|
||||
}
|
||||
}
|
||||
gstate_c.CleanUniforms();
|
||||
return dirty;
|
||||
}
|
||||
|
||||
void ShaderManagerD3D11::BindUniforms() {
|
||||
ID3D11Buffer *vs_cbs[3] = { push_base, push_lights, push_bones };
|
||||
ID3D11Buffer *vs_cbs[2] = { push_base, push_lights };
|
||||
ID3D11Buffer *ps_cbs[1] = { push_base };
|
||||
context_->VSSetConstantBuffers(0, 3, vs_cbs);
|
||||
context_->VSSetConstantBuffers(0, 2, vs_cbs);
|
||||
context_->PSSetConstantBuffers(0, 1, ps_cbs);
|
||||
}
|
||||
|
||||
@@ -207,9 +196,8 @@ void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader **
|
||||
D3D11VertexShader *vs;
|
||||
if (vsIter == vsCache_.end()) {
|
||||
// Vertex shader not in cache. Let's compile it.
|
||||
bool usesLighting;
|
||||
GenerateVertexShaderD3D11(VSID, codeBuffer_, &usesLighting, featureLevel_ <= D3D_FEATURE_LEVEL_9_3 ? HLSL_D3D11_LEVEL9 : HLSL_D3D11);
|
||||
vs = new D3D11VertexShader(device_, featureLevel_, VSID, codeBuffer_, vertType, useHWTransform, usesLighting);
|
||||
GenerateVertexShaderD3D11(VSID, codeBuffer_, featureLevel_ <= D3D_FEATURE_LEVEL_9_3 ? HLSL_D3D11_LEVEL9 : HLSL_D3D11);
|
||||
vs = new D3D11VertexShader(device_, featureLevel_, VSID, codeBuffer_, vertType, useHWTransform);
|
||||
vsCache_[VSID] = vs;
|
||||
} else {
|
||||
vs = vsIter->second;
|
||||
|
||||
@@ -54,19 +54,13 @@ protected:
|
||||
|
||||
class D3D11VertexShader {
|
||||
public:
|
||||
D3D11VertexShader(ID3D11Device *device, D3D_FEATURE_LEVEL featureLevel, VShaderID id, const char *code, int vertType, bool useHWTransform, bool usesLighting);
|
||||
D3D11VertexShader(ID3D11Device *device, D3D_FEATURE_LEVEL featureLevel, VShaderID id, const char *code, int vertType, bool useHWTransform);
|
||||
~D3D11VertexShader();
|
||||
|
||||
const std::string &source() const { return source_; }
|
||||
const std::vector<uint8_t> &bytecode() const { return bytecode_; }
|
||||
bool Failed() const { return failed_; }
|
||||
bool UseHWTransform() const { return useHWTransform_; }
|
||||
bool HasBones() const {
|
||||
return id_.Bit(VS_BIT_ENABLE_BONES);
|
||||
}
|
||||
bool HasLights() const {
|
||||
return usesLighting_;
|
||||
}
|
||||
|
||||
std::string GetShaderString(DebugShaderStringType type) const;
|
||||
ID3D11VertexShader *GetShader() const { return module_; }
|
||||
@@ -80,7 +74,6 @@ protected:
|
||||
|
||||
bool failed_;
|
||||
bool useHWTransform_;
|
||||
bool usesLighting_;
|
||||
VShaderID id_;
|
||||
};
|
||||
|
||||
@@ -108,13 +101,6 @@ public:
|
||||
// Applies dirty changes and copies the buffer.
|
||||
bool IsBaseDirty() { return true; }
|
||||
bool IsLightDirty() { return true; }
|
||||
bool IsBoneDirty() { return true; }
|
||||
|
||||
/*
|
||||
uint32_t PushBaseBuffer(D3D11PushBuffer *dest, VkBuffer *buf);
|
||||
uint32_t PushLightBuffer(D3D11PushBuffer *dest, VkBuffer *buf);
|
||||
uint32_t PushBoneBuffer(D3D11PushBuffer *dest, VkBuffer *buf);
|
||||
*/
|
||||
|
||||
private:
|
||||
void Clear();
|
||||
@@ -134,12 +120,10 @@ private:
|
||||
// Uniform block scratchpad. These (the relevant ones) are copied to the current pushbuffer at draw time.
|
||||
UB_VS_FS_Base ub_base;
|
||||
UB_VS_Lights ub_lights;
|
||||
UB_VS_Bones ub_bones;
|
||||
|
||||
// Not actual pushbuffers, requires D3D11.1, let's try to live without that first.
|
||||
ID3D11Buffer *push_base;
|
||||
ID3D11Buffer *push_lights;
|
||||
ID3D11Buffer *push_bones;
|
||||
|
||||
D3D11FragmentShader *lastFShader_;
|
||||
D3D11VertexShader *lastVShader_;
|
||||
|
||||
@@ -462,4 +462,8 @@ void DrawEngineD3D11::ApplyDrawStateLate(bool applyStencilRef, uint8_t stencilRe
|
||||
context_->OMSetDepthStencilState(depthStencilState_, applyStencilRef ? stencilRef : dynState_.stencilRef);
|
||||
}
|
||||
gstate_c.Clean(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_BLEND_STATE);
|
||||
|
||||
// Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects.
|
||||
if (fboTexBound_)
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE);
|
||||
}
|
||||
@@ -775,22 +775,19 @@ void TextureCacheD3D11::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &
|
||||
FreeAlignedMemory(mapData);
|
||||
}
|
||||
|
||||
bool TextureCacheD3D11::DecodeTexture(u8 *output, const GPUgstate &state) {
|
||||
OutputDebugStringA("TextureCache::DecodeTexture : FixMe\r\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureCacheD3D11::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) {
|
||||
ApplyTexture();
|
||||
SetTexture(false);
|
||||
if (!nextTexture_)
|
||||
return false;
|
||||
|
||||
// Apply texture may need to rebuild the texture if we're about to render, or bind a framebuffer.
|
||||
TexCacheEntry *entry = nextTexture_;
|
||||
ApplyTexture();
|
||||
|
||||
// TODO: Centralize.
|
||||
if (nextTexture_->framebuffer) {
|
||||
VirtualFramebuffer *vfb = nextTexture_->framebuffer;
|
||||
bool flipY = GetGPUBackend() == GPUBackend::OPENGL && g_Config.iRenderingMode != FB_NON_BUFFERED_MODE;
|
||||
buffer.Allocate(vfb->bufferWidth, vfb->bufferHeight, GPU_DBG_FORMAT_8888, flipY);
|
||||
if (entry->framebuffer) {
|
||||
VirtualFramebuffer *vfb = entry->framebuffer;
|
||||
buffer.Allocate(vfb->bufferWidth, vfb->bufferHeight, GPU_DBG_FORMAT_8888, false);
|
||||
bool retval = draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, 0, vfb->bufferWidth, vfb->bufferHeight, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), vfb->bufferWidth);
|
||||
// Vulkan requires us to re-apply all dynamic state for each command buffer, and the above will cause us to start a new cmdbuf.
|
||||
// So let's dirty the things that are involved in Vulkan dynamic state. Readbacks are not frequent so this won't hurt other backends.
|
||||
@@ -800,7 +797,7 @@ bool TextureCacheD3D11::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level
|
||||
return retval;
|
||||
}
|
||||
|
||||
ID3D11Texture2D *texture = (ID3D11Texture2D *)nextTexture_->texturePtr;
|
||||
ID3D11Texture2D *texture = (ID3D11Texture2D *)entry->texturePtr;
|
||||
if (!texture)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -57,9 +57,6 @@ public:
|
||||
shaderManager_ = sm;
|
||||
}
|
||||
|
||||
// Only used by Qt UI?
|
||||
bool DecodeTexture(u8 *output, const GPUgstate &state);
|
||||
|
||||
void ForgetLastTexture() override;
|
||||
void InvalidateLastTexture(TexCacheEntry *entry = nullptr) override;
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "GPU/D3D11/VertexShaderGeneratorD3D11.h"
|
||||
#include "GPU/Directx9/VertexShaderGeneratorDX9.h"
|
||||
|
||||
void GenerateVertexShaderD3D11(const VShaderID &id, char *buffer, bool *usesLighting, ShaderLanguage lang) {
|
||||
*usesLighting = true;
|
||||
void GenerateVertexShaderD3D11(const VShaderID &id, char *buffer, ShaderLanguage lang) {
|
||||
DX9::GenerateVertexShaderHLSL(id, buffer, lang);
|
||||
}
|
||||
@@ -19,4 +19,4 @@
|
||||
|
||||
#include "GPU/Common/ShaderId.h"
|
||||
|
||||
void GenerateVertexShaderD3D11(const VShaderID &id, char *buffer, bool *usesLighting, ShaderLanguage lang);
|
||||
void GenerateVertexShaderD3D11(const VShaderID &id, char *buffer, ShaderLanguage lang);
|
||||
@@ -175,16 +175,6 @@ IDirect3DVertexDeclaration9 *DrawEngineDX9::SetupDecFmtForDraw(VSShader *vshader
|
||||
D3DVERTEXELEMENT9 *VertexElement = &VertexElements[0];
|
||||
|
||||
// Vertices Elements orders
|
||||
// WEIGHT
|
||||
if (decFmt.w0fmt != 0) {
|
||||
VertexAttribSetup(VertexElement, decFmt.w0fmt, decFmt.w0off, D3DDECLUSAGE_TEXCOORD, 1);
|
||||
VertexElement++;
|
||||
}
|
||||
|
||||
if (decFmt.w1fmt != 0) {
|
||||
VertexAttribSetup(VertexElement, decFmt.w1fmt, decFmt.w1off, D3DDECLUSAGE_TEXCOORD, 2);
|
||||
VertexElement++;
|
||||
}
|
||||
|
||||
// TC
|
||||
if (decFmt.uvfmt != 0) {
|
||||
@@ -232,70 +222,6 @@ IDirect3DVertexDeclaration9 *DrawEngineDX9::SetupDecFmtForDraw(VSShader *vshader
|
||||
}
|
||||
}
|
||||
|
||||
void DrawEngineDX9::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) {
|
||||
if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls >= MAX_DEFERRED_DRAW_CALLS || vertexCountInDrawCalls_ + vertexCount > VERTEX_BUFFER_MAX)
|
||||
Flush();
|
||||
|
||||
// TODO: Is this the right thing to do?
|
||||
if (prim == GE_PRIM_KEEP_PREVIOUS) {
|
||||
prim = prevPrim_ != GE_PRIM_INVALID ? prevPrim_ : GE_PRIM_POINTS;
|
||||
} else {
|
||||
prevPrim_ = prim;
|
||||
}
|
||||
|
||||
SetupVertexDecoder(vertType);
|
||||
|
||||
*bytesRead = vertexCount * dec_->VertexSize();
|
||||
|
||||
if ((vertexCount < 2 && prim > 0) || (vertexCount < 3 && prim > 2 && prim != GE_PRIM_RECTANGLES))
|
||||
return;
|
||||
|
||||
DeferredDrawCall &dc = drawCalls[numDrawCalls];
|
||||
dc.verts = verts;
|
||||
dc.inds = inds;
|
||||
dc.vertType = vertType;
|
||||
dc.indexType = (vertType & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT;
|
||||
dc.prim = prim;
|
||||
dc.vertexCount = vertexCount;
|
||||
|
||||
u32 dhash = dcid_;
|
||||
dhash ^= (u32)(uintptr_t)verts;
|
||||
dhash = __rotl(dhash, 13);
|
||||
dhash ^= (u32)(uintptr_t)inds;
|
||||
dhash = __rotl(dhash, 13);
|
||||
dhash ^= (u32)vertType;
|
||||
dhash = __rotl(dhash, 13);
|
||||
dhash ^= (u32)vertexCount;
|
||||
dhash = __rotl(dhash, 13);
|
||||
dhash ^= (u32)prim;
|
||||
dcid_ = dhash;
|
||||
|
||||
if (inds) {
|
||||
GetIndexBounds(inds, vertexCount, vertType, &dc.indexLowerBound, &dc.indexUpperBound);
|
||||
} else {
|
||||
dc.indexLowerBound = 0;
|
||||
dc.indexUpperBound = vertexCount - 1;
|
||||
}
|
||||
|
||||
uvScale[numDrawCalls] = gstate_c.uv;
|
||||
|
||||
numDrawCalls++;
|
||||
vertexCountInDrawCalls_ += vertexCount;
|
||||
|
||||
if (g_Config.bSoftwareSkinning && (vertType & GE_VTYPE_WEIGHT_MASK)) {
|
||||
DecodeVertsStep(decoded, decodeCounter_, decodedVerts_);
|
||||
decodeCounter_++;
|
||||
}
|
||||
|
||||
if (prim == GE_PRIM_RECTANGLES && (gstate.getTextureAddress(0) & 0x3FFFFFFF) == (gstate.getFrameBufAddress() & 0x3FFFFFFF)) {
|
||||
// Rendertarget == texture?
|
||||
if (!g_Config.bDisableSlowFramebufEffects) {
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawEngineDX9::MarkUnreliable(VertexArrayInfoDX9 *vai) {
|
||||
vai->status = VertexArrayInfoDX9::VAI_UNRELIABLE;
|
||||
if (vai->vbo) {
|
||||
@@ -388,8 +314,8 @@ void DrawEngineDX9::DoFlush() {
|
||||
|
||||
// Cannot cache vertex data with morph enabled.
|
||||
bool useCache = g_Config.bVertexCache && !(lastVType_ & GE_VTYPE_MORPHCOUNT_MASK);
|
||||
// Also avoid caching when software skinning.
|
||||
if (g_Config.bSoftwareSkinning && (lastVType_ & GE_VTYPE_WEIGHT_MASK))
|
||||
// Also avoid caching when skinning.
|
||||
if (lastVType_ & GE_VTYPE_WEIGHT_MASK)
|
||||
useCache = false;
|
||||
|
||||
if (useCache) {
|
||||
|
||||
@@ -103,8 +103,6 @@ public:
|
||||
DrawEngineDX9(Draw::DrawContext *draw);
|
||||
virtual ~DrawEngineDX9();
|
||||
|
||||
void SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead);
|
||||
|
||||
void SetShaderManager(ShaderManagerDX9 *shaderManager) {
|
||||
shaderManager_ = shaderManager;
|
||||
}
|
||||
@@ -134,9 +132,6 @@ public:
|
||||
}
|
||||
|
||||
void DispatchFlush() override { Flush(); }
|
||||
void DispatchSubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) override {
|
||||
SubmitPrim(verts, inds, prim, vertexCount, vertType, bytesRead);
|
||||
}
|
||||
|
||||
private:
|
||||
void DoFlush();
|
||||
|
||||
@@ -81,17 +81,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
D3DDECL_END()
|
||||
};
|
||||
|
||||
void FramebufferManagerDX9::DisableState() {
|
||||
dxstate.blend.disable();
|
||||
dxstate.cullMode.set(false, false);
|
||||
dxstate.depthTest.disable();
|
||||
dxstate.scissorTest.disable();
|
||||
dxstate.stencilTest.disable();
|
||||
dxstate.colorMask.set(true, true, true, true);
|
||||
dxstate.stencilMask.set(0xFF);
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE);
|
||||
}
|
||||
|
||||
FramebufferManagerDX9::FramebufferManagerDX9(Draw::DrawContext *draw)
|
||||
: FramebufferManagerCommon(draw),
|
||||
drawPixelsTex_(0),
|
||||
@@ -249,7 +238,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
}
|
||||
|
||||
void FramebufferManagerDX9::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) {
|
||||
// TODO: StretchRect instead?
|
||||
// TODO: StretchRect instead when possible?
|
||||
float coord[20] = {
|
||||
x,y,0, u0,v0,
|
||||
x+w,y,0, u1,v0,
|
||||
@@ -297,7 +286,13 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
}
|
||||
dxstate.texMipLodBias.set(0.0f);
|
||||
dxstate.texMaxMipLevel.set(0);
|
||||
device_->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
||||
dxstate.blend.disable();
|
||||
dxstate.cullMode.set(false, false);
|
||||
dxstate.depthTest.disable();
|
||||
dxstate.scissorTest.disable();
|
||||
dxstate.stencilTest.disable();
|
||||
dxstate.colorMask.set(true, true, true, true);
|
||||
dxstate.stencilMask.set(0xFF);
|
||||
HRESULT hr = device_->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, coord, 5 * sizeof(float));
|
||||
if (FAILED(hr)) {
|
||||
ERROR_LOG_REPORT(G3D, "DrawActiveTexture() failed: %08x", hr);
|
||||
@@ -537,9 +532,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: SSE/NEON
|
||||
// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :)
|
||||
void ConvertFromRGBA8888(u8 *dst, u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format) {
|
||||
void ConvertFromBGRA8888(u8 *dst, u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format) {
|
||||
// Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP.
|
||||
const u32 *src32 = (const u32 *)src;
|
||||
|
||||
@@ -617,7 +610,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
// TODO: Handle the other formats? We don't currently create them, I think.
|
||||
const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp;
|
||||
// Pixel size always 4 here because we always request BGRA8888.
|
||||
ConvertFromRGBA8888(Memory::GetPointer(fb_address + dstByteOffset), (u8 *)locked.pBits, vfb->fb_stride, locked.Pitch / 4, w, h, vfb->format);
|
||||
ConvertFromBGRA8888(Memory::GetPointer(fb_address + dstByteOffset), (u8 *)locked.pBits, vfb->fb_stride, locked.Pitch / 4, w, h, vfb->format);
|
||||
offscreen->UnlockRect();
|
||||
} else {
|
||||
ERROR_LOG_REPORT(G3D, "Unable to lock rect from %08x: %d,%d %dx%d of %dx%d", fb_address, rect.left, rect.top, rect.right, rect.bottom, vfb->renderWidth, vfb->renderHeight);
|
||||
@@ -727,8 +720,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
|
||||
offscreenSurfaces_.clear();
|
||||
|
||||
SetNumExtraFBOs(0);
|
||||
|
||||
DisableState();
|
||||
}
|
||||
|
||||
void FramebufferManagerDX9::Resized() {
|
||||
|
||||
@@ -73,7 +73,6 @@ protected:
|
||||
void Bind2DShader() override;
|
||||
void BindPostShader(const PostShaderUniforms &uniforms) override;
|
||||
void SetViewport2D(int x, int y, int w, int h) override;
|
||||
void DisableState() override;
|
||||
void DecimateFBOs() override;
|
||||
|
||||
// Used by ReadFramebufferToMemory and later framebuffer block copies
|
||||
|
||||
+5
-217
@@ -50,31 +50,6 @@
|
||||
|
||||
namespace DX9 {
|
||||
|
||||
struct D3D9CommandTableEntry {
|
||||
uint8_t cmd;
|
||||
uint8_t flags;
|
||||
uint64_t dirty;
|
||||
GPU_DX9::CmdFunc func;
|
||||
};
|
||||
|
||||
// This table gets crunched into a faster form by init.
|
||||
static const D3D9CommandTableEntry commandTable[] = {
|
||||
// Changes that dirty the current texture.
|
||||
{ GE_CMD_TEXSIZE0, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPUCommon::Execute_TexSize0 },
|
||||
|
||||
// Changing the vertex type requires us to flush.
|
||||
{ GE_CMD_VERTEXTYPE, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_VertexType },
|
||||
|
||||
{ GE_CMD_PRIM, FLAG_EXECUTE, 0, &GPU_DX9::Execute_Prim },
|
||||
{ GE_CMD_BEZIER, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPUCommon::Execute_Bezier },
|
||||
{ GE_CMD_SPLINE, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPUCommon::Execute_Spline },
|
||||
|
||||
// Changes that trigger data copies. Only flushing on change for LOADCLUT must be a bit of a hack...
|
||||
{ GE_CMD_LOADCLUT, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPU_DX9::Execute_LoadClut },
|
||||
};
|
||||
|
||||
GPU_DX9::CommandInfo GPU_DX9::cmdInfo_[256];
|
||||
|
||||
GPU_DX9::GPU_DX9(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
||||
: GPUCommon(gfxCtx, draw),
|
||||
depalShaderCache_(draw),
|
||||
@@ -108,47 +83,7 @@ GPU_DX9::GPU_DX9(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
||||
ERROR_LOG(G3D, "gstate has drifted out of sync!");
|
||||
}
|
||||
|
||||
memset(cmdInfo_, 0, sizeof(cmdInfo_));
|
||||
|
||||
// Import both the global and local command tables, and check for dupes
|
||||
std::set<u8> dupeCheck;
|
||||
for (size_t i = 0; i < commonCommandTableSize; i++) {
|
||||
const u8 cmd = commonCommandTable[i].cmd;
|
||||
if (dupeCheck.find(cmd) != dupeCheck.end()) {
|
||||
ERROR_LOG(G3D, "Command table Dupe: %02x (%i)", (int)cmd, (int)cmd);
|
||||
} else {
|
||||
dupeCheck.insert(cmd);
|
||||
}
|
||||
cmdInfo_[cmd].flags |= (uint64_t)commonCommandTable[i].flags | (commonCommandTable[i].dirty << 8);
|
||||
cmdInfo_[cmd].func = commonCommandTable[i].func;
|
||||
if ((cmdInfo_[cmd].flags & (FLAG_EXECUTE | FLAG_EXECUTEONCHANGE)) && !cmdInfo_[cmd].func) {
|
||||
Crash();
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(commandTable); i++) {
|
||||
const u8 cmd = commandTable[i].cmd;
|
||||
if (dupeCheck.find(cmd) != dupeCheck.end()) {
|
||||
ERROR_LOG(G3D, "Command table Dupe: %02x (%i)", (int)cmd, (int)cmd);
|
||||
} else {
|
||||
dupeCheck.insert(cmd);
|
||||
}
|
||||
cmdInfo_[cmd].flags |= (uint64_t)commandTable[i].flags | (commandTable[i].dirty << 8);
|
||||
cmdInfo_[cmd].func = commandTable[i].func;
|
||||
if ((cmdInfo_[cmd].flags & (FLAG_EXECUTE | FLAG_EXECUTEONCHANGE)) && !cmdInfo_[cmd].func) {
|
||||
Crash();
|
||||
}
|
||||
}
|
||||
// Find commands missing from the table.
|
||||
for (int i = 0; i < 0xEF; i++) {
|
||||
if (dupeCheck.find((u8)i) == dupeCheck.end()) {
|
||||
ERROR_LOG(G3D, "Command missing from table: %02x (%i)", i, i);
|
||||
}
|
||||
}
|
||||
|
||||
// No need to flush before the tex scale/offset commands if we are baking
|
||||
// the tex scale/offset into the vertices anyway.
|
||||
UpdateCmdInfo();
|
||||
CheckGPUFeatures();
|
||||
|
||||
BuildReportingInfo();
|
||||
|
||||
@@ -166,18 +101,6 @@ GPU_DX9::GPU_DX9(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_DX9::UpdateCmdInfo() {
|
||||
if (g_Config.bSoftwareSkinning) {
|
||||
cmdInfo_[GE_CMD_VERTEXTYPE].flags &= ~FLAG_FLUSHBEFOREONCHANGE;
|
||||
cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommon::Execute_VertexTypeSkinning;
|
||||
} else {
|
||||
cmdInfo_[GE_CMD_VERTEXTYPE].flags |= FLAG_FLUSHBEFOREONCHANGE;
|
||||
cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommon::Execute_VertexType;
|
||||
}
|
||||
|
||||
CheckGPUFeatures();
|
||||
}
|
||||
|
||||
void GPU_DX9::CheckGPUFeatures() {
|
||||
u32 features = 0;
|
||||
|
||||
@@ -186,8 +109,9 @@ void GPU_DX9::CheckGPUFeatures() {
|
||||
features |= GPU_SUPPORTS_TEXTURE_LOD_CONTROL;
|
||||
features |= GPU_PREFER_CPU_DOWNLOAD;
|
||||
|
||||
// Accurate depth is required on AMD so we ignore the compat flag to disable it on those. See #9545
|
||||
if (!PSP_CoreParameter().compat.flags().DisableAccurateDepth || draw_->GetDeviceCaps().vendor == Draw::GPUVendor::VENDOR_AMD) {
|
||||
auto vendor = draw_->GetDeviceCaps().vendor;
|
||||
// Accurate depth is required on AMD/nVidia (for reverse Z) so we ignore the compat flag to disable it on those. See #9545
|
||||
if (!PSP_CoreParameter().compat.flags().DisableAccurateDepth || vendor == Draw::GPUVendor::VENDOR_AMD || vendor == Draw::GPUVendor::VENDOR_NVIDIA) {
|
||||
features |= GPU_SUPPORTS_ACCURATE_DEPTH;
|
||||
}
|
||||
|
||||
@@ -264,8 +188,8 @@ void GPU_DX9::InitClear() {
|
||||
|
||||
void GPU_DX9::BeginHostFrame() {
|
||||
GPUCommon::BeginHostFrame();
|
||||
UpdateCmdInfo();
|
||||
if (resized_) {
|
||||
CheckGPUFeatures();
|
||||
framebufferManager_->Resized();
|
||||
drawEngine_.Resized();
|
||||
shaderManagerDX9_->DirtyShader();
|
||||
@@ -305,26 +229,6 @@ void GPU_DX9::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat for
|
||||
framebufferManagerDX9_->SetDisplayFramebuffer(framebuf, stride, format);
|
||||
}
|
||||
|
||||
bool GPU_DX9::FramebufferDirty() {
|
||||
VirtualFramebuffer *vfb = framebufferManager_->GetDisplayVFB();
|
||||
if (vfb) {
|
||||
bool dirty = vfb->dirtyAfterDisplay;
|
||||
vfb->dirtyAfterDisplay = false;
|
||||
return dirty;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU_DX9::FramebufferReallyDirty() {
|
||||
VirtualFramebuffer *vfb = framebufferManager_->GetDisplayVFB();
|
||||
if (vfb) {
|
||||
bool dirty = vfb->reallyDirtyAfterDisplay;
|
||||
vfb->reallyDirtyAfterDisplay = false;
|
||||
return dirty;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPU_DX9::CopyDisplayToOutput() {
|
||||
dxstate.depthWrite.set(true);
|
||||
dxstate.colorMask.set(true, true, true, true);
|
||||
@@ -340,44 +244,6 @@ void GPU_DX9::CopyDisplayToOutput() {
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_IMAGE);
|
||||
}
|
||||
|
||||
// Maybe should write this in ASM...
|
||||
void GPU_DX9::FastRunLoop(DisplayList &list) {
|
||||
PROFILE_THIS_SCOPE("gpuloop");
|
||||
const CommandInfo *cmdInfo = cmdInfo_;
|
||||
int dc = downcount;
|
||||
for (; dc > 0; --dc) {
|
||||
// We know that display list PCs have the upper nibble == 0 - no need to mask the pointer
|
||||
const u32 op = *(const u32 *)(Memory::base + list.pc);
|
||||
const u32 cmd = op >> 24;
|
||||
const CommandInfo &info = cmdInfo[cmd];
|
||||
const u32 diff = op ^ gstate.cmdmem[cmd];
|
||||
if (diff == 0) {
|
||||
if (info.flags & FLAG_EXECUTE) {
|
||||
downcount = dc;
|
||||
(this->*info.func)(op, diff);
|
||||
dc = downcount;
|
||||
}
|
||||
} else {
|
||||
uint64_t flags = info.flags;
|
||||
if (flags & FLAG_FLUSHBEFOREONCHANGE) {
|
||||
drawEngine_.Flush();
|
||||
}
|
||||
gstate.cmdmem[cmd] = op;
|
||||
if (flags & (FLAG_EXECUTE | FLAG_EXECUTEONCHANGE)) {
|
||||
downcount = dc;
|
||||
(this->*info.func)(op, diff);
|
||||
dc = downcount;
|
||||
} else {
|
||||
uint64_t dirty = flags >> 8;
|
||||
if (dirty)
|
||||
gstate_c.Dirty(dirty);
|
||||
}
|
||||
}
|
||||
list.pc += 4;
|
||||
}
|
||||
downcount = 0;
|
||||
}
|
||||
|
||||
void GPU_DX9::FinishDeferred() {
|
||||
// This finishes reading any vertex data that is pending.
|
||||
drawEngine_.FinishDeferred();
|
||||
@@ -410,84 +276,6 @@ void GPU_DX9::ExecuteOp(u32 op, u32 diff) {
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_DX9::Execute_Prim(u32 op, u32 diff) {
|
||||
// This drives all drawing. All other state we just buffer up, then we apply it only
|
||||
// when it's time to draw. As most PSP games set state redundantly ALL THE TIME, this is a huge optimization.
|
||||
|
||||
u32 data = op & 0xFFFFFF;
|
||||
u32 count = data & 0xFFFF;
|
||||
if (count == 0)
|
||||
return;
|
||||
// Upper bits are ignored.
|
||||
GEPrimitiveType prim = static_cast<GEPrimitiveType>((data >> 16) & 7);
|
||||
SetDrawType(DRAW_PRIM, prim);
|
||||
|
||||
// Discard AA lines as we can't do anything that makes sense with these anyway. The SW plugin might, though.
|
||||
|
||||
if (gstate.isAntiAliasEnabled()) {
|
||||
// Discard AA lines in DOA
|
||||
if (prim == GE_PRIM_LINE_STRIP)
|
||||
return;
|
||||
// Discard AA lines in Summon Night 5
|
||||
if ((prim == GE_PRIM_LINES) && gstate.isSkinningEnabled())
|
||||
return;
|
||||
}
|
||||
|
||||
// This also make skipping drawing very effective.
|
||||
framebufferManagerDX9_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason);
|
||||
if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) {
|
||||
drawEngine_.SetupVertexDecoder(gstate.vertType);
|
||||
// Rough estimate, not sure what's correct.
|
||||
cyclesExecuted += EstimatePerVertexCost() * count;
|
||||
return;
|
||||
}
|
||||
|
||||
u32 vertexAddr = gstate_c.vertexAddr;
|
||||
if (!Memory::IsValidAddress(vertexAddr)) {
|
||||
ERROR_LOG_REPORT(G3D, "Bad vertex address %08x!", vertexAddr);
|
||||
return;
|
||||
}
|
||||
|
||||
void *verts = Memory::GetPointerUnchecked(vertexAddr);
|
||||
void *inds = 0;
|
||||
u32 vertexType = gstate.vertType;
|
||||
if ((vertexType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
|
||||
u32 indexAddr = gstate_c.indexAddr;
|
||||
if (!Memory::IsValidAddress(indexAddr)) {
|
||||
ERROR_LOG_REPORT(G3D, "Bad index address %08x!", indexAddr);
|
||||
return;
|
||||
}
|
||||
inds = Memory::GetPointerUnchecked(indexAddr);
|
||||
}
|
||||
|
||||
#ifndef MOBILE_DEVICE
|
||||
if (prim > GE_PRIM_RECTANGLES) {
|
||||
ERROR_LOG_REPORT_ONCE(reportPrim, G3D, "Unexpected prim type: %d", prim);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gstate_c.dirty & DIRTY_VERTEXSHADER_STATE) {
|
||||
vertexCost_ = EstimatePerVertexCost();
|
||||
}
|
||||
gpuStats.vertexGPUCycles += vertexCost_ * count;
|
||||
cyclesExecuted += vertexCost_* count;
|
||||
|
||||
int bytesRead = 0;
|
||||
UpdateUVScaleOffset();
|
||||
drawEngine_.SubmitPrim(verts, inds, prim, count, vertexType, &bytesRead);
|
||||
|
||||
// After drawing, we advance the vertexAddr (when non indexed) or indexAddr (when indexed).
|
||||
// Some games rely on this, they don't bother reloading VADDR and IADDR.
|
||||
// The VADDR/IADDR registers are NOT updated.
|
||||
AdvanceVerts(vertexType, count, bytesRead);
|
||||
}
|
||||
|
||||
void GPU_DX9::Execute_LoadClut(u32 op, u32 diff) {
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);
|
||||
textureCacheDX9_->LoadClut(gstate.getClutAddress(), gstate.getClutLoadBytes());
|
||||
// This could be used to "dirty" textures with clut.
|
||||
}
|
||||
|
||||
void GPU_DX9::GetStats(char *buffer, size_t bufsize) {
|
||||
float vertexAverageCycles = gpuStats.numVertsSubmitted > 0 ? (float)gpuStats.vertexGPUCycles / (float)gpuStats.numVertsSubmitted : 0.0f;
|
||||
snprintf(buffer, bufsize - 1,
|
||||
|
||||
+1
-30
@@ -37,7 +37,7 @@ public:
|
||||
GPU_DX9(GraphicsContext *gfxCtx, Draw::DrawContext *draw);
|
||||
~GPU_DX9();
|
||||
|
||||
void CheckGPUFeatures();
|
||||
void CheckGPUFeatures() override;
|
||||
void PreExecuteOp(u32 op, u32 diff) override;
|
||||
void ExecuteOp(u32 op, u32 diff) override;
|
||||
|
||||
@@ -51,26 +51,6 @@ public:
|
||||
void DoState(PointerWrap &p) override;
|
||||
|
||||
void ClearShaderCache() override;
|
||||
bool DecodeTexture(u8 *dest, const GPUgstate &state) override {
|
||||
return textureCacheDX9_->DecodeTexture(dest, state);
|
||||
}
|
||||
bool FramebufferDirty() override;
|
||||
bool FramebufferReallyDirty() override;
|
||||
|
||||
void GetReportingInfo(std::string &primaryInfo, std::string &fullInfo) override {
|
||||
primaryInfo = reportingPrimaryInfo_;
|
||||
fullInfo = reportingFullInfo_;
|
||||
}
|
||||
|
||||
typedef void (GPU_DX9::*CmdFunc)(u32 op, u32 diff);
|
||||
struct CommandInfo {
|
||||
uint64_t flags;
|
||||
GPU_DX9::CmdFunc func;
|
||||
};
|
||||
|
||||
void Execute_Prim(u32 op, u32 diff);
|
||||
void Execute_TexSize0(u32 op, u32 diff);
|
||||
void Execute_LoadClut(u32 op, u32 diff);
|
||||
|
||||
// Using string because it's generic - makes no assumptions on the size of the shader IDs of this backend.
|
||||
std::vector<std::string> DebugGetShaderIDs(DebugShaderType shader) override;
|
||||
@@ -79,16 +59,12 @@ public:
|
||||
void BeginHostFrame() override;
|
||||
|
||||
protected:
|
||||
void FastRunLoop(DisplayList &list) override;
|
||||
void FinishDeferred() override;
|
||||
|
||||
private:
|
||||
void UpdateCmdInfo();
|
||||
|
||||
void Flush() {
|
||||
drawEngine_.Flush();
|
||||
}
|
||||
// void ApplyDrawState(int prim);
|
||||
void CheckFlushOp(int cmd, u32 diff);
|
||||
void BuildReportingInfo();
|
||||
|
||||
@@ -105,13 +81,8 @@ private:
|
||||
DrawEngineDX9 drawEngine_;
|
||||
ShaderManagerDX9 *shaderManagerDX9_;
|
||||
|
||||
static CommandInfo cmdInfo_[256];
|
||||
|
||||
int lastVsync_;
|
||||
int vertexCost_ = 0;
|
||||
|
||||
std::string reportingPrimaryInfo_;
|
||||
std::string reportingFullInfo_;
|
||||
};
|
||||
|
||||
} // namespace DX9
|
||||
|
||||
@@ -314,7 +314,7 @@ void ShaderManagerDX9::PSUpdateUniforms(u64 dirtyUniforms) {
|
||||
}
|
||||
|
||||
const uint64_t vsUniforms = DIRTY_PROJMATRIX | DIRTY_PROJTHROUGHMATRIX | DIRTY_WORLDMATRIX | DIRTY_VIEWMATRIX | DIRTY_TEXMATRIX |
|
||||
DIRTY_FOGCOEF | DIRTY_BONE_UNIFORMS | DIRTY_UVSCALEOFFSET | DIRTY_DEPTHRANGE |
|
||||
DIRTY_FOGCOEF | DIRTY_UVSCALEOFFSET | DIRTY_DEPTHRANGE |
|
||||
DIRTY_AMBIENT | DIRTY_MATAMBIENTALPHA | DIRTY_MATSPECULAR | DIRTY_MATDIFFUSE | DIRTY_MATEMISSIVE | DIRTY_LIGHT0 | DIRTY_LIGHT1 | DIRTY_LIGHT2 | DIRTY_LIGHT3;
|
||||
|
||||
void ShaderManagerDX9::VSUpdateUniforms(u64 dirtyUniforms) {
|
||||
@@ -388,38 +388,6 @@ void ShaderManagerDX9::VSUpdateUniforms(u64 dirtyUniforms) {
|
||||
ComputeGuardband(gb, 0.0f);
|
||||
VSSetFloatUniform4(CONST_VS_GUARDBAND, gb);
|
||||
}
|
||||
// TODO: Could even set all bones in one go if they're all dirty.
|
||||
#ifdef USE_BONE_ARRAY
|
||||
if (u_bone != 0) {
|
||||
float allBones[8 * 16];
|
||||
|
||||
bool allDirty = true;
|
||||
for (int i = 0; i < numBones; i++) {
|
||||
if (dirtyUniforms & (DIRTY_BONEMATRIX0 << i)) {
|
||||
ConvertMatrix4x3To4x4(allBones + 16 * i, gstate.boneMatrix + 12 * i);
|
||||
} else {
|
||||
allDirty = false;
|
||||
}
|
||||
}
|
||||
if (allDirty) {
|
||||
// Set them all with one call
|
||||
//glUniformMatrix4fv(u_bone, numBones, GL_FALSE, allBones);
|
||||
} else {
|
||||
// Set them one by one. Could try to coalesce two in a row etc but too lazy.
|
||||
for (int i = 0; i < numBones; i++) {
|
||||
if (dirtyUniforms & (DIRTY_BONEMATRIX0 << i)) {
|
||||
//glUniformMatrix4fv(u_bone + i, 1, GL_FALSE, allBones + 16 * i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (dirtyUniforms & (DIRTY_BONEMATRIX0 << i)) {
|
||||
VSSetMatrix4x3_3(CONST_VS_BONE0 + 3 * i, gstate.boneMatrix + 12 * i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Texturing
|
||||
if (dirtyUniforms & DIRTY_UVSCALEOFFSET) {
|
||||
@@ -494,8 +462,8 @@ void ShaderManagerDX9::VSUpdateUniforms(u64 dirtyUniforms) {
|
||||
}
|
||||
VSSetFloat24Uniform3(CONST_VS_LIGHTDIR + i, &gstate.ldir[i * 3]);
|
||||
VSSetFloat24Uniform3(CONST_VS_LIGHTATT + i, &gstate.latt[i * 3]);
|
||||
VSSetFloat(CONST_VS_LIGHTANGLE + i, getFloat24(gstate.lcutoff[i]));
|
||||
VSSetFloat(CONST_VS_LIGHTSPOTCOEF + i, getFloat24(gstate.lconv[i]));
|
||||
float angle_spotCoef[4] = { getFloat24(gstate.lcutoff[i]), getFloat24(gstate.lconv[i]) };
|
||||
VSSetFloatUniform4(CONST_VS_LIGHTANGLE_SPOTCOEF + i, angle_spotCoef);
|
||||
VSSetColorUniform3(CONST_VS_LIGHTAMBIENT + i, gstate.lcolor[i * 3]);
|
||||
VSSetColorUniform3(CONST_VS_LIGHTDIFFUSE + i, gstate.lcolor[i * 3 + 1]);
|
||||
VSSetColorUniform3(CONST_VS_LIGHTSPECULAR + i, gstate.lcolor[i * 3 + 2]);
|
||||
|
||||
@@ -181,7 +181,6 @@ bool FramebufferManagerDX9::NotifyStencilUpload(u32 addr, int size, bool skipZer
|
||||
|
||||
shaderManagerDX9_->DirtyLastShader();
|
||||
|
||||
DisableState();
|
||||
dxstate.colorMask.set(false, false, false, true);
|
||||
dxstate.stencilTest.enable();
|
||||
dxstate.stencilOp.set(D3DSTENCILOP_REPLACE, D3DSTENCILOP_REPLACE, D3DSTENCILOP_REPLACE);
|
||||
|
||||
@@ -171,7 +171,6 @@ void TextureCacheDX9::UpdateSamplingParams(TexCacheEntry &entry, bool force) {
|
||||
dxstate.texMaxMipLevel.set(0);
|
||||
dxstate.texMipLodBias.set(0.0f);
|
||||
}
|
||||
entry.lodBias = lodBias;
|
||||
} else {
|
||||
dxstate.texMaxMipLevel.set(0);
|
||||
dxstate.texMipLodBias.set(0.0f);
|
||||
@@ -788,12 +787,6 @@ void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &re
|
||||
texture->UnlockRect(level);
|
||||
}
|
||||
|
||||
bool TextureCacheDX9::DecodeTexture(u8 *output, const GPUgstate &state)
|
||||
{
|
||||
OutputDebugStringA("TextureCache::DecodeTexture : FixMe\r\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureCacheDX9::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) {
|
||||
SetTexture(true);
|
||||
ApplyTexture();
|
||||
|
||||
@@ -49,9 +49,6 @@ public:
|
||||
shaderManager_ = sm;
|
||||
}
|
||||
|
||||
// Only used by Qt UI?
|
||||
bool DecodeTexture(u8 *output, const GPUgstate &state);
|
||||
|
||||
void ForgetLastTexture() override;
|
||||
void InvalidateLastTexture(TexCacheEntry *entry = nullptr) override;
|
||||
|
||||
|
||||
@@ -38,25 +38,6 @@
|
||||
|
||||
namespace DX9 {
|
||||
|
||||
// The PSP does not have a proper triangle clipper on the sides. It does have on for the front plane.
|
||||
// It has a guard band though and can rasterize rather large
|
||||
// triangles that go outside the viewport. However, there are limits, and it will drop triangles that are very
|
||||
// large. Some games appear to draw broken geometry, probably game bugs that were never discovered because the PSP
|
||||
// would drop the geometry, including Parappa The Rapper in an obscure case and Outrun. Try to get rid of those
|
||||
// triangles by setting the W of one of the vertices to NaN if they are discovered.
|
||||
|
||||
static const char * const boneWeightAttrDecl[9] = {
|
||||
"#ERROR#",
|
||||
"float a_w1:TEXCOORD1;\n",
|
||||
"float2 a_w1:TEXCOORD1;\n",
|
||||
"float3 a_w1:TEXCOORD1;\n",
|
||||
"float4 a_w1:TEXCOORD1;\n",
|
||||
"float4 a_w1:TEXCOORD1;\n float a_w2:TEXCOORD2;\n",
|
||||
"float4 a_w1:TEXCOORD1;\n float2 a_w2:TEXCOORD2;\n",
|
||||
"float4 a_w1:TEXCOORD1;\n float3 a_w2:TEXCOORD2;\n",
|
||||
"float4 a_w1:TEXCOORD1;\n float4 a_w2:TEXCOORD2;\n",
|
||||
};
|
||||
|
||||
enum DoLightComputation {
|
||||
LIGHT_OFF,
|
||||
LIGHT_SHADE,
|
||||
@@ -108,7 +89,6 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage
|
||||
bool flipNormal = id.Bit(VS_BIT_NORM_REVERSE);
|
||||
int ls0 = id.Bits(VS_BIT_LS0, 2);
|
||||
int ls1 = id.Bits(VS_BIT_LS1, 2);
|
||||
bool enableBones = id.Bit(VS_BIT_ENABLE_BONES);
|
||||
bool enableLighting = id.Bit(VS_BIT_LIGHTING_ENABLE);
|
||||
int matUpdate = id.Bits(VS_BIT_MATERIAL_UPDATE, 3);
|
||||
|
||||
@@ -130,12 +110,6 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage
|
||||
}
|
||||
}
|
||||
|
||||
int numBoneWeights = 0;
|
||||
int boneWeightScale = id.Bits(VS_BIT_WEIGHT_FMTSCALE, 2);
|
||||
if (enableBones) {
|
||||
numBoneWeights = 1 + id.Bits(VS_BIT_BONES, 3);
|
||||
}
|
||||
|
||||
if (lang == HLSL_DX9) {
|
||||
WRITE(p, "#pragma warning( disable : 3571 )\n");
|
||||
if (isModeThrough) {
|
||||
@@ -158,15 +132,6 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage
|
||||
WRITE(p, "float4x3 u_view : register(c%i);\n", CONST_VS_VIEW);
|
||||
if (doTextureTransform)
|
||||
WRITE(p, "float4x3 u_tex : register(c%i);\n", CONST_VS_TEXMTX);
|
||||
if (enableBones) {
|
||||
#ifdef USE_BONE_ARRAY
|
||||
WRITE(p, "float4x3 u_bone[%i] : register(c%i);\n", numBones, CONST_VS_BONE0);
|
||||
#else
|
||||
for (int i = 0; i < numBoneWeights; i++) {
|
||||
WRITE(p, "float4x3 u_bone%i : register(c%i);\n", i, CONST_VS_BONE0 + i * 3);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (doTexture) {
|
||||
WRITE(p, "float4 u_uvscaleoffset : register(c%i);\n", CONST_VS_UVSCALEOFFSET);
|
||||
}
|
||||
@@ -184,8 +149,7 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage
|
||||
|
||||
if (type == GE_LIGHTTYPE_SPOT || type == GE_LIGHTTYPE_UNKNOWN) {
|
||||
WRITE(p, "float3 u_lightdir%i : register(c%i);\n", i, CONST_VS_LIGHTDIR + i);
|
||||
WRITE(p, "float4 u_lightangle%i : register(c%i);\n", i, CONST_VS_LIGHTANGLE + i);
|
||||
WRITE(p, "float4 u_lightspotCoef%i : register(c%i);\n", i, CONST_VS_LIGHTSPOTCOEF + i);
|
||||
WRITE(p, "float4 u_lightangle_spotCoef%i : register(c%i);\n", i, CONST_VS_LIGHTANGLE_SPOTCOEF + i);
|
||||
}
|
||||
WRITE(p, "float3 u_lightambient%i : register(c%i);\n", i, CONST_VS_LIGHTAMBIENT + i);
|
||||
WRITE(p, "float3 u_lightdiffuse%i : register(c%i);\n", i, CONST_VS_LIGHTDIFFUSE + i);
|
||||
@@ -211,7 +175,6 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage
|
||||
} else {
|
||||
WRITE(p, "cbuffer base : register(b0) {\n%s};\n", cb_baseStr);
|
||||
WRITE(p, "cbuffer lights: register(b1) {\n%s};\n", cb_vs_lightsStr);
|
||||
WRITE(p, "cbuffer bones : register(b2) {\n%s};\n", cb_vs_bonesStr);
|
||||
}
|
||||
|
||||
// And the "varyings".
|
||||
@@ -221,9 +184,6 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage
|
||||
if ((doSpline || doBezier) && lang == HLSL_D3D11) {
|
||||
WRITE(p, " uint instanceId : SV_InstanceID;\n");
|
||||
}
|
||||
if (enableBones) {
|
||||
WRITE(p, " %s", boneWeightAttrDecl[numBoneWeights]);
|
||||
}
|
||||
if (doTexture && hasTexcoord) {
|
||||
WRITE(p, " float2 texcoord : TEXCOORD0;\n");
|
||||
}
|
||||
@@ -427,184 +387,113 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage
|
||||
}
|
||||
WRITE(p, " Out.gl_Position = outPos;\n");
|
||||
} else {
|
||||
// Step 1: World Transform / Skinning
|
||||
if (!enableBones) {
|
||||
// Hardware tessellation
|
||||
if (doSpline || doBezier) {
|
||||
WRITE(p, " uint num_patches_u = %s;\n", doBezier ? "(u_spline_count_u - 1) / 3u" : "u_spline_count_u - 3");
|
||||
WRITE(p, " float2 tess_pos = In.position.xy;\n");
|
||||
WRITE(p, " int u = In.instanceId %% num_patches_u;\n");
|
||||
WRITE(p, " int v = In.instanceId / num_patches_u;\n");
|
||||
WRITE(p, " int2 patch_pos = int2(u, v);\n");
|
||||
WRITE(p, " float3 _pos[16];\n");
|
||||
WRITE(p, " float2 _tex[16];\n");
|
||||
WRITE(p, " float4 _col[16];\n");
|
||||
WRITE(p, " int idx;\n");
|
||||
WRITE(p, " int2 index;\n");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
WRITE(p, " idx = (%i + v%s) * u_spline_count_u + (%i + u%s);\n", i, doBezier ? " * 3" : "", j, doBezier ? " * 3" : "");
|
||||
WRITE(p, " index = int2(idx, 0);\n");
|
||||
WRITE(p, " _pos[%i] = u_tess_pos_tex.Load(index).xyz;\n", i * 4 + j);
|
||||
if (doTexture && hasTexcoord && hasTexcoordTess)
|
||||
WRITE(p, " _tex[%i] = u_tess_tex_tex.Load(index).xy;\n", i * 4 + j);
|
||||
if (hasColor && hasColorTess)
|
||||
WRITE(p, " _col[%i] = u_tess_col_tex.Load(index).rgba;\n", i * 4 + j);
|
||||
}
|
||||
}
|
||||
WRITE(p, " float2 weights[4];\n");
|
||||
if (doBezier) {
|
||||
// Bernstein 3D
|
||||
WRITE(p, " weights[0] = (1.0 - tess_pos) * (1.0 - tess_pos) * (1.0 - tess_pos);\n");
|
||||
WRITE(p, " weights[1] = 3.0 * tess_pos * (1.0 - tess_pos) * (1.0 - tess_pos);\n");
|
||||
WRITE(p, " weights[2] = 3.0 * tess_pos * tess_pos * (1.0 - tess_pos);\n");
|
||||
WRITE(p, " weights[3] = tess_pos * tess_pos * tess_pos;\n");
|
||||
} else if (doSpline) {
|
||||
WRITE(p, " int2 spline_num_patches = int2(u_spline_count_u - 3, u_spline_count_v - 3);\n");
|
||||
WRITE(p, " int2 spline_type = int2(u_spline_type_u, u_spline_type_v);\n");
|
||||
WRITE(p, " float2 knots[6];\n");
|
||||
WRITE(p, " spline_knot(spline_num_patches, spline_type, knots, patch_pos);\n");
|
||||
WRITE(p, " spline_weight(tess_pos + patch_pos, knots, weights);\n");
|
||||
}
|
||||
WRITE(p, " float3 pos = tess_sample(_pos, weights);\n");
|
||||
if (doTexture && hasTexcoord) {
|
||||
if (hasTexcoordTess)
|
||||
WRITE(p, " float2 tex = tess_sample(_tex, weights);\n");
|
||||
else
|
||||
WRITE(p, " float2 tex = tess_pos + patch_pos;\n");
|
||||
}
|
||||
if (hasColor) {
|
||||
if (hasColorTess)
|
||||
WRITE(p, " float4 col = tess_sample(_col, weights);\n");
|
||||
else
|
||||
WRITE(p, " float4 col = u_tess_col_tex.Load(int2(0, 0)).rgba;\n");
|
||||
}
|
||||
if (hasNormal) {
|
||||
// Curved surface is probably always need to compute normal(not sampling from control points)
|
||||
if (doBezier) {
|
||||
// Bernstein derivative
|
||||
WRITE(p, " float2 bernderiv[4];\n");
|
||||
WRITE(p, " bernderiv[0] = -3.0 * (tess_pos - 1.0) * (tess_pos - 1.0); \n");
|
||||
WRITE(p, " bernderiv[1] = 9.0 * tess_pos * tess_pos - 12.0 * tess_pos + 3.0; \n");
|
||||
WRITE(p, " bernderiv[2] = 3.0 * (2.0 - 3.0 * tess_pos) * tess_pos; \n");
|
||||
WRITE(p, " bernderiv[3] = 3.0 * tess_pos * tess_pos; \n");
|
||||
|
||||
WRITE(p, " float2 bernderiv_u[4];\n");
|
||||
WRITE(p, " float2 bernderiv_v[4];\n");
|
||||
WRITE(p, " for (int i = 0; i < 4; i++) {\n");
|
||||
WRITE(p, " bernderiv_u[i] = float2(bernderiv[i].x, weights[i].y);\n");
|
||||
WRITE(p, " bernderiv_v[i] = float2(weights[i].x, bernderiv[i].y);\n");
|
||||
WRITE(p, " }\n");
|
||||
|
||||
WRITE(p, " float3 du = tess_sample(_pos, bernderiv_u);\n");
|
||||
WRITE(p, " float3 dv = tess_sample(_pos, bernderiv_v);\n");
|
||||
} else if (doSpline) {
|
||||
WRITE(p, " float2 tess_next_u = float2(In.normal.x, 0.0);\n");
|
||||
WRITE(p, " float2 tess_next_v = float2(0.0, In.normal.y);\n");
|
||||
// Right
|
||||
WRITE(p, " float2 tess_pos_r = tess_pos + tess_next_u;\n");
|
||||
WRITE(p, " spline_weight(tess_pos_r + patch_pos, knots, weights);\n");
|
||||
WRITE(p, " float3 pos_r = tess_sample(_pos, weights);\n");
|
||||
// Left
|
||||
WRITE(p, " float2 tess_pos_l = tess_pos - tess_next_u;\n");
|
||||
WRITE(p, " spline_weight(tess_pos_l + patch_pos, knots, weights);\n");
|
||||
WRITE(p, " float3 pos_l = tess_sample(_pos, weights);\n");
|
||||
// Down
|
||||
WRITE(p, " float2 tess_pos_d = tess_pos + tess_next_v;\n");
|
||||
WRITE(p, " spline_weight(tess_pos_d + patch_pos, knots, weights);\n");
|
||||
WRITE(p, " float3 pos_d = tess_sample(_pos, weights);\n");
|
||||
// Up
|
||||
WRITE(p, " float2 tess_pos_u = tess_pos - tess_next_v;\n");
|
||||
WRITE(p, " spline_weight(tess_pos_u + patch_pos, knots, weights);\n");
|
||||
WRITE(p, " float3 pos_u = tess_sample(_pos, weights);\n");
|
||||
|
||||
WRITE(p, " float3 du = pos_r - pos_l;\n");
|
||||
WRITE(p, " float3 dv = pos_d - pos_u;\n");
|
||||
}
|
||||
WRITE(p, " float3 nrm = cross(du, dv);\n");
|
||||
WRITE(p, " nrm = normalize(nrm);\n");
|
||||
}
|
||||
WRITE(p, " float3 worldpos = mul(float4(pos.xyz, 1.0), u_world);\n");
|
||||
if (hasNormal)
|
||||
WRITE(p, " float3 worldnormal = normalize(mul(float4(%snrm, 0.0), u_world));\n", flipNormalTess ? "-" : "");
|
||||
else
|
||||
WRITE(p, " float3 worldnormal = float3(0.0, 0.0, 1.0);\n");
|
||||
} else {
|
||||
// No skinning, just standard T&L.
|
||||
WRITE(p, " float3 worldpos = mul(float4(In.position.xyz, 1.0), u_world);\n");
|
||||
if (hasNormal)
|
||||
WRITE(p, " float3 worldnormal = normalize(mul(float4(%sIn.normal, 0.0), u_world));\n", flipNormal ? "-" : "");
|
||||
else
|
||||
WRITE(p, " float3 worldnormal = float3(0.0, 0.0, 1.0);\n");
|
||||
}
|
||||
} else {
|
||||
static const char * const boneWeightAttr[8] = {
|
||||
"a_w1.x", "a_w1.y", "a_w1.z", "a_w1.w",
|
||||
"a_w2.x", "a_w2.y", "a_w2.z", "a_w2.w",
|
||||
};
|
||||
|
||||
#if defined(USE_FOR_LOOP) && defined(USE_BONE_ARRAY)
|
||||
|
||||
// To loop through the weights, we unfortunately need to put them in a float array.
|
||||
// GLSL ES sucks - no way to directly initialize an array!
|
||||
switch (numBoneWeights) {
|
||||
case 1: WRITE(p, " float w[1]; w[0] = a_w1;\n"); break;
|
||||
case 2: WRITE(p, " float w[2]; w[0] = a_w1.x; w[1] = a_w1.y;\n"); break;
|
||||
case 3: WRITE(p, " float w[3]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z;\n"); break;
|
||||
case 4: WRITE(p, " float w[4]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z; w[3] = a_w1.w;\n"); break;
|
||||
case 5: WRITE(p, " float w[5]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z; w[3] = a_w1.w; w[4] = a_w2;\n"); break;
|
||||
case 6: WRITE(p, " float w[6]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z; w[3] = a_w1.w; w[4] = a_w2.x; w[5] = a_w2.y;\n"); break;
|
||||
case 7: WRITE(p, " float w[7]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z; w[3] = a_w1.w; w[4] = a_w2.x; w[5] = a_w2.y; w[6] = a_w2.z;\n"); break;
|
||||
case 8: WRITE(p, " float w[8]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z; w[3] = a_w1.w; w[4] = a_w2.x; w[5] = a_w2.y; w[6] = a_w2.z; w[7] = a_w2.w;\n"); break;
|
||||
}
|
||||
|
||||
WRITE(p, " mat4 skinMatrix = w[0] * u_bone[0];\n");
|
||||
if (numBoneWeights > 1) {
|
||||
WRITE(p, " for (int i = 1; i < %i; i++) {\n", numBoneWeights);
|
||||
WRITE(p, " skinMatrix += w[i] * u_bone[i];\n");
|
||||
WRITE(p, " }\n");
|
||||
}
|
||||
|
||||
#else
|
||||
if (lang == HLSL_D3D11 || lang == HLSL_D3D11_LEVEL9) {
|
||||
if (numBoneWeights == 1)
|
||||
WRITE(p, " float4x3 skinMatrix = mul(In.a_w1, u_bone[0])");
|
||||
else
|
||||
WRITE(p, " float4x3 skinMatrix = mul(In.a_w1.x, u_bone[0])");
|
||||
for (int i = 1; i < numBoneWeights; i++) {
|
||||
const char *weightAttr = boneWeightAttr[i];
|
||||
// workaround for "cant do .x of scalar" issue
|
||||
if (numBoneWeights == 1 && i == 0) weightAttr = "a_w1";
|
||||
if (numBoneWeights == 5 && i == 4) weightAttr = "a_w2";
|
||||
WRITE(p, " + mul(In.%s, u_bone[%i])", weightAttr, i);
|
||||
}
|
||||
} else {
|
||||
if (numBoneWeights == 1)
|
||||
WRITE(p, " float4x3 skinMatrix = mul(In.a_w1, u_bone0)");
|
||||
else
|
||||
WRITE(p, " float4x3 skinMatrix = mul(In.a_w1.x, u_bone0)");
|
||||
for (int i = 1; i < numBoneWeights; i++) {
|
||||
const char *weightAttr = boneWeightAttr[i];
|
||||
// workaround for "cant do .x of scalar" issue
|
||||
if (numBoneWeights == 1 && i == 0) weightAttr = "a_w1";
|
||||
if (numBoneWeights == 5 && i == 4) weightAttr = "a_w2";
|
||||
WRITE(p, " + mul(In.%s, u_bone%i)", weightAttr, i);
|
||||
// Step 1: World Transform
|
||||
// Hardware tessellation
|
||||
if (doSpline || doBezier) {
|
||||
WRITE(p, " uint num_patches_u = %s;\n", doBezier ? "(u_spline_count_u - 1) / 3u" : "u_spline_count_u - 3");
|
||||
WRITE(p, " float2 tess_pos = In.position.xy;\n");
|
||||
WRITE(p, " int u = In.instanceId %% num_patches_u;\n");
|
||||
WRITE(p, " int v = In.instanceId / num_patches_u;\n");
|
||||
WRITE(p, " int2 patch_pos = int2(u, v);\n");
|
||||
WRITE(p, " float3 _pos[16];\n");
|
||||
WRITE(p, " float2 _tex[16];\n");
|
||||
WRITE(p, " float4 _col[16];\n");
|
||||
WRITE(p, " int idx;\n");
|
||||
WRITE(p, " int2 index;\n");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
WRITE(p, " idx = (%i + v%s) * u_spline_count_u + (%i + u%s);\n", i, doBezier ? " * 3" : "", j, doBezier ? " * 3" : "");
|
||||
WRITE(p, " index = int2(idx, 0);\n");
|
||||
WRITE(p, " _pos[%i] = u_tess_pos_tex.Load(index).xyz;\n", i * 4 + j);
|
||||
if (doTexture && hasTexcoord && hasTexcoordTess)
|
||||
WRITE(p, " _tex[%i] = u_tess_tex_tex.Load(index).xy;\n", i * 4 + j);
|
||||
if (hasColor && hasColorTess)
|
||||
WRITE(p, " _col[%i] = u_tess_col_tex.Load(index).rgba;\n", i * 4 + j);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
WRITE(p, ";\n");
|
||||
|
||||
// Trying to simplify this results in bugs in LBP...
|
||||
WRITE(p, " float3 skinnedpos = mul(float4(In.position.xyz, 1.0), skinMatrix);\n");
|
||||
WRITE(p, " float3 worldpos = mul(float4(skinnedpos, 1.0), u_world);\n");
|
||||
|
||||
WRITE(p, " float2 weights[4];\n");
|
||||
if (doBezier) {
|
||||
// Bernstein 3D
|
||||
WRITE(p, " weights[0] = (1.0 - tess_pos) * (1.0 - tess_pos) * (1.0 - tess_pos);\n");
|
||||
WRITE(p, " weights[1] = 3.0 * tess_pos * (1.0 - tess_pos) * (1.0 - tess_pos);\n");
|
||||
WRITE(p, " weights[2] = 3.0 * tess_pos * tess_pos * (1.0 - tess_pos);\n");
|
||||
WRITE(p, " weights[3] = tess_pos * tess_pos * tess_pos;\n");
|
||||
} else if (doSpline) {
|
||||
WRITE(p, " int2 spline_num_patches = int2(u_spline_count_u - 3, u_spline_count_v - 3);\n");
|
||||
WRITE(p, " int2 spline_type = int2(u_spline_type_u, u_spline_type_v);\n");
|
||||
WRITE(p, " float2 knots[6];\n");
|
||||
WRITE(p, " spline_knot(spline_num_patches, spline_type, knots, patch_pos);\n");
|
||||
WRITE(p, " spline_weight(tess_pos + patch_pos, knots, weights);\n");
|
||||
}
|
||||
WRITE(p, " float3 pos = tess_sample(_pos, weights);\n");
|
||||
if (doTexture && hasTexcoord) {
|
||||
if (hasTexcoordTess)
|
||||
WRITE(p, " float2 tex = tess_sample(_tex, weights);\n");
|
||||
else
|
||||
WRITE(p, " float2 tex = tess_pos + patch_pos;\n");
|
||||
}
|
||||
if (hasColor) {
|
||||
if (hasColorTess)
|
||||
WRITE(p, " float4 col = tess_sample(_col, weights);\n");
|
||||
else
|
||||
WRITE(p, " float4 col = u_tess_col_tex.Load(int2(0, 0)).rgba;\n");
|
||||
}
|
||||
if (hasNormal) {
|
||||
WRITE(p, " float3 skinnednormal = mul(float4(%sIn.normal, 0.0), skinMatrix);\n", flipNormal ? "-" : "");
|
||||
} else {
|
||||
WRITE(p, " float3 skinnednormal = mul(float4(0.0, 0.0, %s1.0, 0.0), skinMatrix);\n", flipNormal ? "-" : "");
|
||||
// Curved surface is probably always need to compute normal(not sampling from control points)
|
||||
if (doBezier) {
|
||||
// Bernstein derivative
|
||||
WRITE(p, " float2 bernderiv[4];\n");
|
||||
WRITE(p, " bernderiv[0] = -3.0 * (tess_pos - 1.0) * (tess_pos - 1.0); \n");
|
||||
WRITE(p, " bernderiv[1] = 9.0 * tess_pos * tess_pos - 12.0 * tess_pos + 3.0; \n");
|
||||
WRITE(p, " bernderiv[2] = 3.0 * (2.0 - 3.0 * tess_pos) * tess_pos; \n");
|
||||
WRITE(p, " bernderiv[3] = 3.0 * tess_pos * tess_pos; \n");
|
||||
|
||||
WRITE(p, " float2 bernderiv_u[4];\n");
|
||||
WRITE(p, " float2 bernderiv_v[4];\n");
|
||||
WRITE(p, " for (int i = 0; i < 4; i++) {\n");
|
||||
WRITE(p, " bernderiv_u[i] = float2(bernderiv[i].x, weights[i].y);\n");
|
||||
WRITE(p, " bernderiv_v[i] = float2(weights[i].x, bernderiv[i].y);\n");
|
||||
WRITE(p, " }\n");
|
||||
|
||||
WRITE(p, " float3 du = tess_sample(_pos, bernderiv_u);\n");
|
||||
WRITE(p, " float3 dv = tess_sample(_pos, bernderiv_v);\n");
|
||||
} else if (doSpline) {
|
||||
WRITE(p, " float2 tess_next_u = float2(In.normal.x, 0.0);\n");
|
||||
WRITE(p, " float2 tess_next_v = float2(0.0, In.normal.y);\n");
|
||||
// Right
|
||||
WRITE(p, " float2 tess_pos_r = tess_pos + tess_next_u;\n");
|
||||
WRITE(p, " spline_weight(tess_pos_r + patch_pos, knots, weights);\n");
|
||||
WRITE(p, " float3 pos_r = tess_sample(_pos, weights);\n");
|
||||
// Left
|
||||
WRITE(p, " float2 tess_pos_l = tess_pos - tess_next_u;\n");
|
||||
WRITE(p, " spline_weight(tess_pos_l + patch_pos, knots, weights);\n");
|
||||
WRITE(p, " float3 pos_l = tess_sample(_pos, weights);\n");
|
||||
// Down
|
||||
WRITE(p, " float2 tess_pos_d = tess_pos + tess_next_v;\n");
|
||||
WRITE(p, " spline_weight(tess_pos_d + patch_pos, knots, weights);\n");
|
||||
WRITE(p, " float3 pos_d = tess_sample(_pos, weights);\n");
|
||||
// Up
|
||||
WRITE(p, " float2 tess_pos_u = tess_pos - tess_next_v;\n");
|
||||
WRITE(p, " spline_weight(tess_pos_u + patch_pos, knots, weights);\n");
|
||||
WRITE(p, " float3 pos_u = tess_sample(_pos, weights);\n");
|
||||
|
||||
WRITE(p, " float3 du = pos_r - pos_l;\n");
|
||||
WRITE(p, " float3 dv = pos_d - pos_u;\n");
|
||||
}
|
||||
WRITE(p, " float3 nrm = cross(du, dv);\n");
|
||||
WRITE(p, " nrm = normalize(nrm);\n");
|
||||
}
|
||||
WRITE(p, " float3 worldnormal = normalize(mul(float4(skinnednormal, 0.0), u_world));\n");
|
||||
WRITE(p, " float3 worldpos = mul(float4(pos.xyz, 1.0), u_world);\n");
|
||||
if (hasNormal)
|
||||
WRITE(p, " float3 worldnormal = normalize(mul(float4(%snrm, 0.0), u_world));\n", flipNormalTess ? "-" : "");
|
||||
else
|
||||
WRITE(p, " float3 worldnormal = float3(0.0, 0.0, 1.0);\n");
|
||||
} else {
|
||||
WRITE(p, " float3 worldpos = mul(float4(In.position.xyz, 1.0), u_world);\n");
|
||||
if (hasNormal)
|
||||
WRITE(p, " float3 worldnormal = normalize(mul(float4(%sIn.normal, 0.0), u_world));\n", flipNormal ? "-" : "");
|
||||
else
|
||||
WRITE(p, " float3 worldnormal = float3(0.0, 0.0, 1.0);\n");
|
||||
}
|
||||
|
||||
WRITE(p, " float4 viewPos = float4(mul(float4(worldpos, 1.0), u_view), 1.0);\n");
|
||||
@@ -713,8 +602,8 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage
|
||||
case GE_LIGHTTYPE_SPOT:
|
||||
case GE_LIGHTTYPE_UNKNOWN:
|
||||
WRITE(p, " float angle%i = dot(normalize(u_lightdir%i), toLight);\n", i, i);
|
||||
WRITE(p, " if (angle%i >= u_lightangle%i.x) {\n", i, i);
|
||||
WRITE(p, " lightScale = clamp(1.0 / dot(u_lightatt%i, float3(1.0, distance, distance*distance)), 0.0, 1.0) * pow(angle%i, u_lightspotCoef%i.x);\n", i, i, i);
|
||||
WRITE(p, " if (angle%i >= u_lightangle_spotCoef%i.x) {\n", i, i);
|
||||
WRITE(p, " lightScale = clamp(1.0 / dot(u_lightatt%i, float3(1.0, distance, distance*distance)), 0.0, 1.0) * pow(angle%i, u_lightangle_spotCoef%i.y);\n", i, i, i);
|
||||
WRITE(p, " } else {\n");
|
||||
WRITE(p, " lightScale = 0.0;\n");
|
||||
WRITE(p, " }\n");
|
||||
|
||||
@@ -21,40 +21,30 @@
|
||||
|
||||
namespace DX9 {
|
||||
|
||||
// #define USE_BONE_ARRAY
|
||||
|
||||
void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage lang = HLSL_DX9);
|
||||
|
||||
#define CONST_VS_PROJ 0
|
||||
#define CONST_VS_PROJ_THROUGH 4
|
||||
#define CONST_VS_VIEW 8
|
||||
#define CONST_VS_WORLD 11
|
||||
#define CONST_VS_TEXMTX 14
|
||||
#define CONST_VS_UVSCALEOFFSET 17
|
||||
#define CONST_VS_FOGCOEF 18
|
||||
#define CONST_VS_AMBIENT 19
|
||||
#define CONST_VS_BONE0 20
|
||||
#define CONST_VS_BONE1 23
|
||||
#define CONST_VS_BONE2 26
|
||||
#define CONST_VS_BONE3 29
|
||||
#define CONST_VS_BONE4 32
|
||||
#define CONST_VS_BONE5 35
|
||||
#define CONST_VS_BONE6 38
|
||||
#define CONST_VS_BONE7 41
|
||||
#define CONST_VS_BONE8 44
|
||||
#define CONST_VS_MATAMBIENTALPHA 47
|
||||
#define CONST_VS_MATDIFFUSE 48
|
||||
#define CONST_VS_MATSPECULAR 49
|
||||
#define CONST_VS_MATEMISSIVE 50
|
||||
#define CONST_VS_LIGHTPOS 51
|
||||
#define CONST_VS_LIGHTDIR 55
|
||||
#define CONST_VS_LIGHTATT 59
|
||||
#define CONST_VS_LIGHTANGLE 63
|
||||
#define CONST_VS_LIGHTSPOTCOEF 67
|
||||
#define CONST_VS_LIGHTDIFFUSE 71
|
||||
#define CONST_VS_LIGHTSPECULAR 75
|
||||
#define CONST_VS_LIGHTAMBIENT 79
|
||||
#define CONST_VS_DEPTHRANGE 83
|
||||
enum {
|
||||
CONST_VS_PROJ = 0,
|
||||
CONST_VS_PROJ_THROUGH = 4,
|
||||
CONST_VS_VIEW = 8,
|
||||
CONST_VS_WORLD = 11,
|
||||
CONST_VS_TEXMTX = 14,
|
||||
CONST_VS_UVSCALEOFFSET = 17,
|
||||
CONST_VS_FOGCOEF = 18,
|
||||
CONST_VS_AMBIENT = 19,
|
||||
CONST_VS_MATAMBIENTALPHA = 20,
|
||||
CONST_VS_MATDIFFUSE = 21,
|
||||
CONST_VS_MATSPECULAR = 22,
|
||||
CONST_VS_MATEMISSIVE = 23,
|
||||
CONST_VS_LIGHTPOS = 24,
|
||||
CONST_VS_LIGHTDIR = 28,
|
||||
CONST_VS_LIGHTATT = 32,
|
||||
CONST_VS_LIGHTANGLE_SPOTCOEF = 36,
|
||||
CONST_VS_LIGHTDIFFUSE = 40,
|
||||
CONST_VS_LIGHTSPECULAR = 44,
|
||||
CONST_VS_LIGHTAMBIENT = 48,
|
||||
CONST_VS_DEPTHRANGE = 52,
|
||||
};
|
||||
#define CONST_VS_GUARDBAND 84
|
||||
|
||||
};
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "Core/Reporting.h"
|
||||
#include "DepalettizeShaderGLES.h"
|
||||
#include "GPU/GLES/TextureCacheGLES.h"
|
||||
#include "ext/native/gfx/GLStateCache.h"
|
||||
#include "GPU/Common/DepalettizeShaderCommon.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -58,38 +57,8 @@ static const char *depalVShader300 =
|
||||
" gl_Position = a_position;\n"
|
||||
"}\n";
|
||||
|
||||
|
||||
static bool CheckShaderCompileSuccess(GLuint shader, const char *code) {
|
||||
GLint success;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
#define MAX_INFO_LOG_SIZE 2048
|
||||
GLchar infoLog[MAX_INFO_LOG_SIZE];
|
||||
GLsizei len;
|
||||
glGetShaderInfoLog(shader, MAX_INFO_LOG_SIZE, &len, infoLog);
|
||||
infoLog[len] = '\0';
|
||||
#ifdef __ANDROID__
|
||||
ELOG("Error in shader compilation! %s\n", infoLog);
|
||||
ELOG("Shader source:\n%s\n", (const char *)code);
|
||||
#endif
|
||||
ERROR_LOG(G3D, "Error in shader compilation!\n");
|
||||
ERROR_LOG(G3D, "Info log: %s\n", infoLog);
|
||||
ERROR_LOG(G3D, "Shader source:\n%s\n", (const char *)code);
|
||||
#ifdef SHADERLOG
|
||||
OutputDebugStringUTF8(infoLog);
|
||||
#endif
|
||||
shader = 0;
|
||||
return false;
|
||||
} else {
|
||||
DEBUG_LOG(G3D, "Compiled shader:\n%s\n", (const char *)code);
|
||||
#ifdef SHADERLOG
|
||||
OutputDebugStringUTF8(code);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
DepalShaderCacheGLES::DepalShaderCacheGLES() {
|
||||
DepalShaderCacheGLES::DepalShaderCacheGLES(Draw::DrawContext *draw) {
|
||||
render_ = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
// Pre-build the vertex program
|
||||
useGL3_ = gl_extensions.GLES3 || gl_extensions.VersionGEThan(3, 3);
|
||||
|
||||
@@ -102,25 +71,12 @@ DepalShaderCacheGLES::~DepalShaderCacheGLES() {
|
||||
}
|
||||
|
||||
bool DepalShaderCacheGLES::CreateVertexShader() {
|
||||
if (vertexShaderFailed_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vertexShader_ = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(vertexShader_, 1, useGL3_ ? &depalVShader300 : &depalVShader100, 0);
|
||||
glCompileShader(vertexShader_);
|
||||
|
||||
if (!CheckShaderCompileSuccess(vertexShader_, useGL3_ ? depalVShader300 : depalVShader100)) {
|
||||
glDeleteShader(vertexShader_);
|
||||
vertexShader_ = 0;
|
||||
// Don't try to recompile.
|
||||
vertexShaderFailed_ = true;
|
||||
}
|
||||
|
||||
return !vertexShaderFailed_;
|
||||
std::string src(useGL3_ ? depalVShader300 : depalVShader100);
|
||||
vertexShader_ = render_->CreateShader(GL_VERTEX_SHADER, src, "depal");
|
||||
return true;
|
||||
}
|
||||
|
||||
GLuint DepalShaderCacheGLES::GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, u32 *rawClut) {
|
||||
GLRTexture *DepalShaderCacheGLES::GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, u32 *rawClut) {
|
||||
u32 clutId = GetClutID(clutFormat, clutHash);
|
||||
|
||||
auto oldtex = texCache_.find(clutId);
|
||||
@@ -133,18 +89,13 @@ GLuint DepalShaderCacheGLES::GetClutTexture(GEPaletteFormat clutFormat, const u3
|
||||
int texturePixels = clutFormat == GE_CMODE_32BIT_ABGR8888 ? 256 : 512;
|
||||
|
||||
DepalTexture *tex = new DepalTexture();
|
||||
glGenTextures(1, &tex->texture);
|
||||
glBindTexture(GL_TEXTURE_2D, tex->texture);
|
||||
tex->texture = render_->CreateTexture(GL_TEXTURE_2D);
|
||||
GLuint components = dstFmt == GL_UNSIGNED_SHORT_5_6_5 ? GL_RGB : GL_RGBA;
|
||||
|
||||
GLuint components2 = components;
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, components, texturePixels, 1, 0, components2, dstFmt, (void *)rawClut);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
uint8_t *clutCopy = new uint8_t[1024];
|
||||
memcpy(clutCopy, rawClut, 1024);
|
||||
render_->TextureImage(tex->texture, 0, texturePixels, 1, components, components2, dstFmt, clutCopy, GLRAllocType::NEW, false);
|
||||
|
||||
tex->lastFrame = gpuStats.numFlips;
|
||||
texCache_[clutId] = tex;
|
||||
@@ -153,20 +104,20 @@ GLuint DepalShaderCacheGLES::GetClutTexture(GEPaletteFormat clutFormat, const u3
|
||||
|
||||
void DepalShaderCacheGLES::Clear() {
|
||||
for (auto shader = cache_.begin(); shader != cache_.end(); ++shader) {
|
||||
glDeleteShader(shader->second->fragShader);
|
||||
render_->DeleteShader(shader->second->fragShader);
|
||||
if (shader->second->program) {
|
||||
glDeleteProgram(shader->second->program);
|
||||
render_->DeleteProgram(shader->second->program);
|
||||
}
|
||||
delete shader->second;
|
||||
}
|
||||
cache_.clear();
|
||||
for (auto tex = texCache_.begin(); tex != texCache_.end(); ++tex) {
|
||||
glDeleteTextures(1, &tex->second->texture);
|
||||
render_->DeleteTexture(tex->second->texture);
|
||||
delete tex->second;
|
||||
}
|
||||
texCache_.clear();
|
||||
if (vertexShader_) {
|
||||
glDeleteShader(vertexShader_);
|
||||
render_->DeleteShader(vertexShader_);
|
||||
vertexShader_ = 0;
|
||||
}
|
||||
}
|
||||
@@ -174,7 +125,7 @@ void DepalShaderCacheGLES::Clear() {
|
||||
void DepalShaderCacheGLES::Decimate() {
|
||||
for (auto tex = texCache_.begin(); tex != texCache_.end(); ) {
|
||||
if (tex->second->lastFrame + DEPAL_TEXTURE_OLD_AGE < gpuStats.numFlips) {
|
||||
glDeleteTextures(1, &tex->second->texture);
|
||||
render_->DeleteTexture(tex->second->texture);
|
||||
delete tex->second;
|
||||
texCache_.erase(tex++);
|
||||
} else {
|
||||
@@ -188,10 +139,14 @@ DepalShader *DepalShaderCacheGLES::GetDepalettizeShader(uint32_t clutMode, GEBuf
|
||||
|
||||
auto shader = cache_.find(id);
|
||||
if (shader != cache_.end()) {
|
||||
DepalShader *depal = shader->second;
|
||||
// If compile failed previously, try to recover.
|
||||
if (depal->fragShader->failed || vertexShader_->failed)
|
||||
return nullptr;
|
||||
return shader->second;
|
||||
}
|
||||
|
||||
if (vertexShader_ == 0) {
|
||||
if (!vertexShader_) {
|
||||
if (!CreateVertexShader()) {
|
||||
// The vertex shader failed, no need to bother trying the fragment.
|
||||
return nullptr;
|
||||
@@ -201,64 +156,33 @@ DepalShader *DepalShaderCacheGLES::GetDepalettizeShader(uint32_t clutMode, GEBuf
|
||||
char *buffer = new char[2048];
|
||||
|
||||
GenerateDepalShader(buffer, pixelFormat, useGL3_ ? GLSL_300 : GLSL_140);
|
||||
|
||||
GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
|
||||
const char *buf = buffer;
|
||||
glShaderSource(fragShader, 1, &buf, 0);
|
||||
glCompileShader(fragShader);
|
||||
|
||||
CheckShaderCompileSuccess(fragShader, buffer);
|
||||
|
||||
GLuint program = glCreateProgram();
|
||||
glAttachShader(program, vertexShader_);
|
||||
glAttachShader(program, fragShader);
|
||||
|
||||
glBindAttribLocation(program, 0, "a_position");
|
||||
glBindAttribLocation(program, 1, "a_texcoord0");
|
||||
|
||||
glLinkProgram(program);
|
||||
glUseProgram(program);
|
||||
|
||||
GLint u_tex = glGetUniformLocation(program, "tex");
|
||||
GLint u_pal = glGetUniformLocation(program, "pal");
|
||||
|
||||
glUniform1i(u_tex, 0);
|
||||
glUniform1i(u_pal, 3);
|
||||
std::string src(buffer);
|
||||
GLRShader *fragShader = render_->CreateShader(GL_FRAGMENT_SHADER, src, "depal");
|
||||
|
||||
DepalShader *depal = new DepalShader();
|
||||
|
||||
std::vector<GLRProgram::Semantic> semantics;
|
||||
semantics.push_back({ 0, "a_position" });
|
||||
semantics.push_back({ 1, "a_texcoord0" });
|
||||
|
||||
std::vector<GLRProgram::UniformLocQuery> queries;
|
||||
queries.push_back({ &depal->u_tex, "tex" });
|
||||
queries.push_back({ &depal->u_pal, "pal" });
|
||||
|
||||
std::vector<GLRProgram::Initializer> initializer;
|
||||
initializer.push_back({ &depal->u_tex, 0, 0 });
|
||||
initializer.push_back({ &depal->u_pal, 0, 3 });
|
||||
|
||||
std::vector<GLRShader *> shaders{ vertexShader_, fragShader };
|
||||
|
||||
GLRProgram *program = render_->CreateProgram(shaders, semantics, queries, initializer, false);
|
||||
|
||||
depal->program = program;
|
||||
depal->fragShader = fragShader;
|
||||
depal->code = buffer;
|
||||
cache_[id] = depal;
|
||||
|
||||
GLint linkStatus = GL_FALSE;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
|
||||
if (linkStatus != GL_TRUE) {
|
||||
GLint bufLength = 0;
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
|
||||
if (bufLength) {
|
||||
char* errorbuf = new char[bufLength];
|
||||
glGetProgramInfoLog(program, bufLength, NULL, errorbuf);
|
||||
#ifdef SHADERLOG
|
||||
OutputDebugStringUTF8(buffer);
|
||||
OutputDebugStringUTF8(errorbuf);
|
||||
#endif
|
||||
ERROR_LOG(G3D, "Could not link program:\n %s \n\n %s", errorbuf, buf);
|
||||
delete[] errorbuf; // we're dead!
|
||||
}
|
||||
|
||||
// Since it failed, let's mark it in the cache so we don't keep retrying.
|
||||
// That will only make it slower.
|
||||
depal->program = 0;
|
||||
|
||||
// We will delete the shader later in Clear().
|
||||
glDeleteProgram(program);
|
||||
} else {
|
||||
depal->a_position = glGetAttribLocation(program, "a_position");
|
||||
depal->a_texcoord0 = glGetAttribLocation(program, "a_texcoord0");
|
||||
}
|
||||
|
||||
delete[] buffer;
|
||||
return depal->program ? depal : nullptr;
|
||||
}
|
||||
|
||||
@@ -19,34 +19,36 @@
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "gfx/gl_common.h"
|
||||
#include "thin3d/thin3d.h"
|
||||
#include "thin3d/GLRenderManager.h"
|
||||
#include "GPU/ge_constants.h"
|
||||
#include "GPU/Common/ShaderCommon.h"
|
||||
#include "GPU/Common/DepalettizeShaderCommon.h"
|
||||
|
||||
class DepalShader {
|
||||
public:
|
||||
GLuint program;
|
||||
GLuint fragShader;
|
||||
GLint a_position;
|
||||
GLint a_texcoord0;
|
||||
GLRProgram *program;
|
||||
GLRShader *fragShader;
|
||||
GLint u_tex;
|
||||
GLint u_pal;
|
||||
std::string code;
|
||||
};
|
||||
|
||||
class DepalTexture {
|
||||
public:
|
||||
GLuint texture;
|
||||
GLRTexture *texture;
|
||||
int lastFrame;
|
||||
};
|
||||
|
||||
// Caches both shaders and palette textures.
|
||||
class DepalShaderCacheGLES : public DepalShaderCacheCommon {
|
||||
public:
|
||||
DepalShaderCacheGLES();
|
||||
DepalShaderCacheGLES(Draw::DrawContext *draw);
|
||||
~DepalShaderCacheGLES();
|
||||
|
||||
// This also uploads the palette and binds the correct texture.
|
||||
DepalShader *GetDepalettizeShader(uint32_t clutMode, GEBufferFormat pixelFormat);
|
||||
GLuint GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, u32 *rawClut);
|
||||
GLRTexture *GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, u32 *rawClut);
|
||||
void Clear();
|
||||
void Decimate();
|
||||
std::vector<std::string> DebugGetShaderIDs(DebugShaderType type);
|
||||
@@ -55,9 +57,10 @@ public:
|
||||
private:
|
||||
bool CreateVertexShader();
|
||||
|
||||
GLRenderManager *render_;
|
||||
bool useGL3_;
|
||||
bool vertexShaderFailed_;
|
||||
GLuint vertexShader_;
|
||||
GLRShader *vertexShader_;
|
||||
std::map<u32, DepalShader *> cache_;
|
||||
std::map<u32, DepalTexture *> texCache_;
|
||||
};
|
||||
|
||||
+204
-496
@@ -15,52 +15,6 @@
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
// Ideas for speeding things up on mobile OpenGL ES implementations
|
||||
//
|
||||
// Use superbuffers! Yes I just invented that name.
|
||||
//
|
||||
// The idea is to avoid respecifying the vertex format between every draw call (multiple glVertexAttribPointer ...)
|
||||
// by combining the contents of multiple draw calls into one buffer, as long as
|
||||
// they have exactly the same output vertex format. (different input formats is fine! This way
|
||||
// we can combine the data for multiple draws with different numbers of bones, as we consider numbones < 4 to be = 4)
|
||||
// into one VBO.
|
||||
//
|
||||
// This will likely be a win because I believe that between every change of VBO + glVertexAttribPointer*N, the driver will
|
||||
// perform a lot of validation, probably at draw call time, while all the validation can be skipped if the only thing
|
||||
// that changes between two draw calls is simple state or texture or a matrix etc, not anything vertex related.
|
||||
// Also the driver will have to manage hundreds instead of thousands of VBOs in games like GTA.
|
||||
//
|
||||
// * Every 10 frames or something, do the following:
|
||||
// - Frame 1:
|
||||
// + Mark all drawn buffers with in-frame sequence numbers (alternatively,
|
||||
// just log them in an array)
|
||||
// - Frame 2 (beginning?):
|
||||
// + Take adjacent buffers that have the same output vertex format, and add them
|
||||
// to a list of buffers to combine. Create said buffers with appropriate sizes
|
||||
// and precompute the offsets that the draws should be written into.
|
||||
// - Frame 2 (end):
|
||||
// + Actually do the work of combining the buffers. This probably means re-decoding
|
||||
// the vertices into a new one. Will also have to apply index offsets.
|
||||
//
|
||||
// Also need to change the drawing code so that we don't glBindBuffer and respecify glVAP if
|
||||
// two subsequent drawcalls come from the same superbuffer.
|
||||
//
|
||||
// Or we ignore all of this including vertex caching and simply find a way to do highly optimized vertex streaming,
|
||||
// like Dolphin is trying to. That will likely never be able to reach the same speed as perfectly optimized
|
||||
// superbuffers though. For this we will have to JIT the vertex decoder but that's not too hard.
|
||||
//
|
||||
// Now, when do we delete superbuffers? Maybe when half the buffers within have been killed?
|
||||
//
|
||||
// Another idea for GTA which switches textures a lot while not changing much other state is to use ES 3 Array
|
||||
// textures, if they are the same size (even if they aren't, might be okay to simply resize the textures to match
|
||||
// if they're just a multiple of 2 away) or something. Then we'd have to add a W texture coordinate to choose the
|
||||
// texture within the bound texture array to the vertex data when merging into superbuffers.
|
||||
//
|
||||
// There are even more things to try. For games that do matrix palette skinning by quickly switching bones and
|
||||
// just drawing a few triangles per call (NBA, FF:CC, Tekken 6 etc) we could even collect matrices, upload them
|
||||
// all at once, writing matrix indices into the vertices in addition to the weights, and then doing a single
|
||||
// draw call with specially generated shader to draw the whole mesh. This code will be seriously complex though.
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/timeutil.h"
|
||||
|
||||
@@ -83,7 +37,6 @@
|
||||
#include "GPU/Common/SplineCommon.h"
|
||||
#include "GPU/Common/VertexDecoderCommon.h"
|
||||
#include "GPU/Common/SoftwareTransformCommon.h"
|
||||
#include "ext/native/gfx/GLStateCache.h"
|
||||
#include "GPU/GLES/FragmentTestCacheGLES.h"
|
||||
#include "GPU/GLES/StateMappingGLES.h"
|
||||
#include "GPU/GLES/TextureCacheGLES.h"
|
||||
@@ -91,7 +44,7 @@
|
||||
#include "GPU/GLES/ShaderManagerGLES.h"
|
||||
#include "GPU/GLES/GPU_GLES.h"
|
||||
|
||||
extern const GLuint glprim[8] = {
|
||||
const GLuint glprim[8] = {
|
||||
GL_POINTS,
|
||||
GL_LINES,
|
||||
GL_LINE_STRIP,
|
||||
@@ -115,7 +68,8 @@ enum {
|
||||
|
||||
enum { VAI_KILL_AGE = 120, VAI_UNRELIABLE_KILL_AGE = 240, VAI_UNRELIABLE_KILL_MAX = 4 };
|
||||
|
||||
DrawEngineGLES::DrawEngineGLES() : vai_(256) {
|
||||
DrawEngineGLES::DrawEngineGLES(Draw::DrawContext *draw) : vai_(256), draw_(draw), inputLayoutMap_(16) {
|
||||
render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
|
||||
decOptions_.expandAllWeightsToFloat = false;
|
||||
decOptions_.expand8BitNormalsToFloat = false;
|
||||
@@ -133,7 +87,7 @@ DrawEngineGLES::DrawEngineGLES() : vai_(256) {
|
||||
|
||||
InitDeviceObjects();
|
||||
|
||||
tessDataTransfer = new TessellationDataTransferGLES(gl_extensions.VersionGEThan(3, 0, 0));
|
||||
tessDataTransfer = new TessellationDataTransferGLES(render_);
|
||||
}
|
||||
|
||||
DrawEngineGLES::~DrawEngineGLES() {
|
||||
@@ -145,17 +99,6 @@ DrawEngineGLES::~DrawEngineGLES() {
|
||||
delete tessDataTransfer;
|
||||
}
|
||||
|
||||
void DrawEngineGLES::RestoreVAO() {
|
||||
if (sharedVao_ != 0) {
|
||||
glBindVertexArray(sharedVao_);
|
||||
} else if (gstate_c.Supports(GPU_SUPPORTS_VAO)) {
|
||||
// Note: this is here because, InitDeviceObjects() is called before GPU_SUPPORTS_VAO is setup.
|
||||
// So, this establishes it if Supports() returns true and there isn't one yet.
|
||||
glGenVertexArrays(1, &sharedVao_);
|
||||
glBindVertexArray(sharedVao_);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawEngineGLES::DeviceLost() {
|
||||
DestroyDeviceObjects();
|
||||
}
|
||||
@@ -165,35 +108,66 @@ void DrawEngineGLES::DeviceRestore() {
|
||||
}
|
||||
|
||||
void DrawEngineGLES::InitDeviceObjects() {
|
||||
if (bufferNameCache_.empty()) {
|
||||
bufferNameCache_.resize(VERTEXCACHE_NAME_CACHE_SIZE);
|
||||
glGenBuffers(VERTEXCACHE_NAME_CACHE_SIZE, &bufferNameCache_[0]);
|
||||
bufferNameCacheSize_ = 0;
|
||||
for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) {
|
||||
frameData_[i].pushVertex = new GLPushBuffer(render_, GL_ARRAY_BUFFER, 1024 * 1024);
|
||||
frameData_[i].pushIndex = new GLPushBuffer(render_, GL_ELEMENT_ARRAY_BUFFER, 256 * 1024);
|
||||
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_VAO)) {
|
||||
glGenVertexArrays(1, &sharedVao_);
|
||||
} else {
|
||||
sharedVao_ = 0;
|
||||
}
|
||||
} else {
|
||||
ERROR_LOG(G3D, "Device objects already initialized!");
|
||||
render_->RegisterPushBuffer(i, frameData_[i].pushVertex);
|
||||
render_->RegisterPushBuffer(i, frameData_[i].pushIndex);
|
||||
}
|
||||
|
||||
int vertexSize = sizeof(TransformedVertex);
|
||||
std::vector<GLRInputLayout::Entry> entries;
|
||||
entries.push_back({ ATTR_POSITION, 4, GL_FLOAT, GL_FALSE, vertexSize, 0 });
|
||||
entries.push_back({ ATTR_TEXCOORD, 3, GL_FLOAT, GL_FALSE, vertexSize, offsetof(TransformedVertex, u) });
|
||||
entries.push_back({ ATTR_COLOR0, 4, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, offsetof(TransformedVertex, color0) });
|
||||
entries.push_back({ ATTR_COLOR1, 3, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, offsetof(TransformedVertex, color1) });
|
||||
softwareInputLayout_ = render_->CreateInputLayout(entries);
|
||||
}
|
||||
|
||||
void DrawEngineGLES::DestroyDeviceObjects() {
|
||||
ClearTrackedVertexArrays();
|
||||
if (!bufferNameCache_.empty()) {
|
||||
glstate.arrayBuffer.unbind();
|
||||
glstate.elementArrayBuffer.unbind();
|
||||
glDeleteBuffers((GLsizei)bufferNameCache_.size(), &bufferNameCache_[0]);
|
||||
bufferNameCache_.clear();
|
||||
bufferNameInfo_.clear();
|
||||
freeSizedBuffers_.clear();
|
||||
bufferNameCacheSize_ = 0;
|
||||
if (sharedVao_ != 0) {
|
||||
glDeleteVertexArrays(1, &sharedVao_);
|
||||
}
|
||||
// Beware: this could be called twice in a row, sometimes.
|
||||
for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) {
|
||||
if (!frameData_[i].pushVertex && !frameData_[i].pushIndex)
|
||||
continue;
|
||||
|
||||
render_->UnregisterPushBuffer(i, frameData_[i].pushVertex);
|
||||
render_->UnregisterPushBuffer(i, frameData_[i].pushIndex);
|
||||
frameData_[i].pushVertex->Destroy();
|
||||
frameData_[i].pushIndex->Destroy();
|
||||
delete frameData_[i].pushVertex;
|
||||
delete frameData_[i].pushIndex;
|
||||
frameData_[i].pushVertex = nullptr;
|
||||
frameData_[i].pushIndex = nullptr;
|
||||
}
|
||||
|
||||
ClearTrackedVertexArrays();
|
||||
|
||||
if (softwareInputLayout_)
|
||||
render_->DeleteInputLayout(softwareInputLayout_);
|
||||
softwareInputLayout_ = nullptr;
|
||||
|
||||
ClearInputLayoutMap();
|
||||
}
|
||||
|
||||
void DrawEngineGLES::ClearInputLayoutMap() {
|
||||
inputLayoutMap_.Iterate([&](const uint32_t &key, GLRInputLayout *il) {
|
||||
render_->DeleteInputLayout(il);
|
||||
});
|
||||
inputLayoutMap_.Clear();
|
||||
}
|
||||
|
||||
void DrawEngineGLES::BeginFrame() {
|
||||
FrameData &frameData = frameData_[render_->GetCurFrame()];
|
||||
frameData.pushIndex->Begin();
|
||||
frameData.pushVertex->Begin();
|
||||
}
|
||||
|
||||
void DrawEngineGLES::EndFrame() {
|
||||
FrameData &frameData = frameData_[render_->GetCurFrame()];
|
||||
frameData.pushIndex->End();
|
||||
frameData.pushVertex->End();
|
||||
tessDataTransfer->EndFrame();
|
||||
}
|
||||
|
||||
struct GlTypeInfo {
|
||||
@@ -220,98 +194,59 @@ static const GlTypeInfo GLComp[] = {
|
||||
{GL_UNSIGNED_SHORT, 4, GL_TRUE},// DEC_U16_4,
|
||||
};
|
||||
|
||||
static inline void VertexAttribSetup(int attrib, int fmt, int stride, u8 *ptr) {
|
||||
static inline void VertexAttribSetup(int attrib, int fmt, int stride, int offset, std::vector<GLRInputLayout::Entry> &entries) {
|
||||
if (fmt) {
|
||||
const GlTypeInfo &type = GLComp[fmt];
|
||||
glVertexAttribPointer(attrib, type.count, type.type, type.normalized, stride, ptr);
|
||||
GLRInputLayout::Entry entry;
|
||||
entry.offset = offset;
|
||||
entry.location = attrib;
|
||||
entry.normalized = type.normalized;
|
||||
entry.type = type.type;
|
||||
entry.stride = stride;
|
||||
entry.count = type.count;
|
||||
entries.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Use VBO and get rid of the vertexData pointers - with that, we will supply only offsets
|
||||
static void SetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt, u8 *vertexData) {
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
VertexAttribSetup(ATTR_W1, decFmt.w0fmt, decFmt.stride, vertexData + decFmt.w0off);
|
||||
VertexAttribSetup(ATTR_W2, decFmt.w1fmt, decFmt.stride, vertexData + decFmt.w1off);
|
||||
VertexAttribSetup(ATTR_TEXCOORD, decFmt.uvfmt, decFmt.stride, vertexData + decFmt.uvoff);
|
||||
VertexAttribSetup(ATTR_COLOR0, decFmt.c0fmt, decFmt.stride, vertexData + decFmt.c0off);
|
||||
VertexAttribSetup(ATTR_COLOR1, decFmt.c1fmt, decFmt.stride, vertexData + decFmt.c1off);
|
||||
VertexAttribSetup(ATTR_NORMAL, decFmt.nrmfmt, decFmt.stride, vertexData + decFmt.nrmoff);
|
||||
VertexAttribSetup(ATTR_POSITION, decFmt.posfmt, decFmt.stride, vertexData + decFmt.posoff);
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
GLRInputLayout *DrawEngineGLES::SetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt) {
|
||||
uint32_t key = decFmt.id;
|
||||
GLRInputLayout *inputLayout = inputLayoutMap_.Get(key);
|
||||
if (inputLayout) {
|
||||
return inputLayout;
|
||||
}
|
||||
|
||||
std::vector<GLRInputLayout::Entry> entries;
|
||||
VertexAttribSetup(ATTR_TEXCOORD, decFmt.uvfmt, decFmt.stride, decFmt.uvoff, entries);
|
||||
VertexAttribSetup(ATTR_COLOR0, decFmt.c0fmt, decFmt.stride, decFmt.c0off, entries);
|
||||
VertexAttribSetup(ATTR_COLOR1, decFmt.c1fmt, decFmt.stride, decFmt.c1off, entries);
|
||||
VertexAttribSetup(ATTR_NORMAL, decFmt.nrmfmt, decFmt.stride, decFmt.nrmoff, entries);
|
||||
VertexAttribSetup(ATTR_POSITION, decFmt.posfmt, decFmt.stride, decFmt.posoff, entries);
|
||||
|
||||
inputLayout = render_->CreateInputLayout(entries);
|
||||
inputLayoutMap_.Insert(key, inputLayout);
|
||||
return inputLayout;
|
||||
}
|
||||
|
||||
void DrawEngineGLES::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) {
|
||||
if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls >= MAX_DEFERRED_DRAW_CALLS || vertexCountInDrawCalls_ + vertexCount > VERTEX_BUFFER_MAX)
|
||||
Flush();
|
||||
void DrawEngineGLES::DecodeVertsToPushBuffer(GLPushBuffer *push, uint32_t *bindOffset, GLRBuffer **buf) {
|
||||
u8 *dest = decoded;
|
||||
|
||||
// TODO: Is this the right thing to do?
|
||||
if (prim == GE_PRIM_KEEP_PREVIOUS) {
|
||||
prim = prevPrim_ != GE_PRIM_INVALID ? prevPrim_ : GE_PRIM_POINTS;
|
||||
} else {
|
||||
prevPrim_ = prim;
|
||||
}
|
||||
|
||||
SetupVertexDecoder(vertType);
|
||||
|
||||
*bytesRead = vertexCount * dec_->VertexSize();
|
||||
|
||||
if ((vertexCount < 2 && prim > 0) || (vertexCount < 3 && prim > 2 && prim != GE_PRIM_RECTANGLES))
|
||||
return;
|
||||
|
||||
DeferredDrawCall &dc = drawCalls[numDrawCalls];
|
||||
dc.verts = verts;
|
||||
dc.inds = inds;
|
||||
dc.vertType = vertType;
|
||||
dc.indexType = (vertType & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT;
|
||||
dc.prim = prim;
|
||||
dc.vertexCount = vertexCount;
|
||||
|
||||
u32 dhash = dcid_;
|
||||
dhash ^= (u32)(uintptr_t)verts;
|
||||
dhash = __rotl(dhash, 13);
|
||||
dhash ^= (u32)(uintptr_t)inds;
|
||||
dhash = __rotl(dhash, 13);
|
||||
dhash ^= (u32)vertType;
|
||||
dhash = __rotl(dhash, 13);
|
||||
dhash ^= (u32)vertexCount;
|
||||
dhash = __rotl(dhash, 13);
|
||||
dhash ^= (u32)prim;
|
||||
dcid_ = dhash;
|
||||
|
||||
if (inds) {
|
||||
GetIndexBounds(inds, vertexCount, vertType, &dc.indexLowerBound, &dc.indexUpperBound);
|
||||
} else {
|
||||
dc.indexLowerBound = 0;
|
||||
dc.indexUpperBound = vertexCount - 1;
|
||||
}
|
||||
|
||||
uvScale[numDrawCalls] = gstate_c.uv;
|
||||
|
||||
numDrawCalls++;
|
||||
vertexCountInDrawCalls_ += vertexCount;
|
||||
|
||||
if (g_Config.bSoftwareSkinning && (vertType & GE_VTYPE_WEIGHT_MASK)) {
|
||||
DecodeVertsStep(decoded, decodeCounter_, decodedVerts_);
|
||||
decodeCounter_++;
|
||||
}
|
||||
|
||||
if (prim == GE_PRIM_RECTANGLES && (gstate.getTextureAddress(0) & 0x3FFFFFFF) == (gstate.getFrameBufAddress() & 0x3FFFFFFF)) {
|
||||
// Rendertarget == texture?
|
||||
if (!g_Config.bDisableSlowFramebufEffects) {
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);
|
||||
Flush();
|
||||
}
|
||||
// Figure out how much pushbuffer space we need to allocate.
|
||||
if (push) {
|
||||
int vertsToDecode = ComputeNumVertsToDecode();
|
||||
dest = (u8 *)push->Push(vertsToDecode * dec_->GetDecVtxFmt().stride, bindOffset, buf);
|
||||
}
|
||||
DecodeVerts(dest);
|
||||
}
|
||||
|
||||
void DrawEngineGLES::MarkUnreliable(VertexArrayInfo *vai) {
|
||||
vai->status = VertexArrayInfo::VAI_UNRELIABLE;
|
||||
if (vai->vbo) {
|
||||
FreeBuffer(vai->vbo);
|
||||
render_->DeleteBuffer(vai->vbo);
|
||||
vai->vbo = 0;
|
||||
}
|
||||
if (vai->ebo) {
|
||||
FreeBuffer(vai->ebo);
|
||||
render_->DeleteBuffer(vai->ebo);
|
||||
vai->ebo = 0;
|
||||
}
|
||||
}
|
||||
@@ -351,108 +286,55 @@ void DrawEngineGLES::DecimateTrackedVertexArrays() {
|
||||
vai_.Maintain();
|
||||
}
|
||||
|
||||
GLuint DrawEngineGLES::AllocateBuffer(size_t sz) {
|
||||
GLuint unused = 0;
|
||||
|
||||
auto freeMatch = freeSizedBuffers_.find(sz);
|
||||
if (freeMatch != freeSizedBuffers_.end()) {
|
||||
unused = freeMatch->second;
|
||||
_assert_(!bufferNameInfo_[unused].used);
|
||||
|
||||
freeSizedBuffers_.erase(freeMatch);
|
||||
} else {
|
||||
for (GLuint buf : bufferNameCache_) {
|
||||
const BufferNameInfo &info = bufferNameInfo_[buf];
|
||||
if (info.used) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Just pick the first unused one, we'll have to resize it.
|
||||
unused = buf;
|
||||
|
||||
// Let's also remove from the free list, if it's there.
|
||||
if (info.sz != 0) {
|
||||
auto range = freeSizedBuffers_.equal_range(info.sz);
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
if (it->second == buf) {
|
||||
// It will only be once, so remove and bail.
|
||||
freeSizedBuffers_.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (unused == 0) {
|
||||
size_t oldSize = bufferNameCache_.size();
|
||||
bufferNameCache_.resize(oldSize + VERTEXCACHE_NAME_CACHE_SIZE);
|
||||
glGenBuffers(VERTEXCACHE_NAME_CACHE_SIZE, &bufferNameCache_[oldSize]);
|
||||
|
||||
unused = bufferNameCache_[oldSize];
|
||||
}
|
||||
|
||||
BufferNameInfo &info = bufferNameInfo_[unused];
|
||||
|
||||
// Record the change in size.
|
||||
bufferNameCacheSize_ += sz - info.sz;
|
||||
info.sz = sz;
|
||||
info.used = true;
|
||||
return unused;
|
||||
}
|
||||
|
||||
void DrawEngineGLES::FreeBuffer(GLuint buf) {
|
||||
// We can reuse buffers by setting new data on them, so let's actually keep it.
|
||||
auto it = bufferNameInfo_.find(buf);
|
||||
if (it != bufferNameInfo_.end()) {
|
||||
it->second.used = false;
|
||||
it->second.lastFrame = gpuStats.numFlips;
|
||||
|
||||
if (it->second.sz != 0) {
|
||||
freeSizedBuffers_.insert(std::make_pair(it->second.sz, buf));
|
||||
}
|
||||
} else {
|
||||
ERROR_LOG(G3D, "Unexpected buffer freed (%d) but not tracked", buf);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawEngineGLES::FreeVertexArray(VertexArrayInfo *vai) {
|
||||
if (vai->vbo) {
|
||||
FreeBuffer(vai->vbo);
|
||||
vai->vbo = 0;
|
||||
render_->DeleteBuffer(vai->vbo);
|
||||
vai->vbo = nullptr;
|
||||
}
|
||||
if (vai->ebo) {
|
||||
FreeBuffer(vai->ebo);
|
||||
vai->ebo = 0;
|
||||
render_->DeleteBuffer(vai->ebo);
|
||||
vai->ebo = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawEngineGLES::DoFlush() {
|
||||
PROFILE_THIS_SCOPE("flush");
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
|
||||
FrameData &frameData = frameData_[render_->GetCurFrame()];
|
||||
|
||||
gpuStats.numFlushes++;
|
||||
gpuStats.numTrackedVertexArrays = (int)vai_.size();
|
||||
|
||||
bool textureNeedsApply = false;
|
||||
if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) {
|
||||
textureCache_->SetTexture();
|
||||
gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);
|
||||
textureNeedsApply = true;
|
||||
}
|
||||
|
||||
GEPrimitiveType prim = prevPrim_;
|
||||
ApplyDrawState(prim);
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
|
||||
VShaderID vsid;
|
||||
Shader *vshader = shaderManager_->ApplyVertexShader(prim, lastVType_, &vsid);
|
||||
|
||||
GLRBuffer *vertexBuffer = nullptr;
|
||||
GLRBuffer *indexBuffer = nullptr;
|
||||
uint32_t vertexBufferOffset = 0;
|
||||
uint32_t indexBufferOffset = 0;
|
||||
|
||||
if (vshader->UseHWTransform()) {
|
||||
GLuint vbo = 0, ebo = 0;
|
||||
int vertexCount = 0;
|
||||
bool useElements = true;
|
||||
|
||||
// Cannot cache vertex data with morph enabled.
|
||||
bool useCache = g_Config.bVertexCache && !(lastVType_ & GE_VTYPE_MORPHCOUNT_MASK);
|
||||
// Also avoid caching when software skinning.
|
||||
if (g_Config.bSoftwareSkinning && (lastVType_ & GE_VTYPE_WEIGHT_MASK))
|
||||
// Also avoid caching when skinning.
|
||||
if (lastVType_ & GE_VTYPE_WEIGHT_MASK)
|
||||
useCache = false;
|
||||
|
||||
// TEMPORARY
|
||||
useCache = false;
|
||||
|
||||
if (useCache) {
|
||||
u32 id = dcid_ ^ gstate.getUVGenMode(); // This can have an effect on which UV decoder we need to use! And hence what the decoded data will look like. See #9263
|
||||
VertexArrayInfo *vai = vai_.Get(id);
|
||||
@@ -534,31 +416,27 @@ void DrawEngineGLES::DoFlush() {
|
||||
_dbg_assert_msg_(G3D, gstate_c.vertBounds.minV >= gstate_c.vertBounds.maxV, "Should not have checked UVs when caching.");
|
||||
|
||||
size_t vsz = dec_->GetDecVtxFmt().stride * indexGen.MaxIndex();
|
||||
vai->vbo = AllocateBuffer(vsz);
|
||||
glstate.arrayBuffer.bind(vai->vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, vsz, decoded, GL_STATIC_DRAW);
|
||||
vai->vbo = render_->CreateBuffer(GL_ARRAY_BUFFER, vsz, GL_STATIC_DRAW);
|
||||
render_->BufferSubdata(vai->vbo, 0, vsz, decoded);
|
||||
// If there's only been one primitive type, and it's either TRIANGLES, LINES or POINTS,
|
||||
// there is no need for the index buffer we built. We can then use glDrawArrays instead
|
||||
// for a very minor speed boost.
|
||||
if (useElements) {
|
||||
size_t esz = sizeof(short) * indexGen.VertexCount();
|
||||
vai->ebo = AllocateBuffer(esz);
|
||||
glstate.elementArrayBuffer.bind(vai->ebo);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, esz, (GLvoid *)decIndex, GL_STATIC_DRAW);
|
||||
vai->ebo = render_->CreateBuffer(GL_ARRAY_BUFFER, esz, GL_STATIC_DRAW);
|
||||
render_->BufferSubdata(vai->ebo, 0, esz, (uint8_t *)decIndex, false);
|
||||
} else {
|
||||
vai->ebo = 0;
|
||||
glstate.elementArrayBuffer.bind(vai->ebo);
|
||||
render_->BindIndexBuffer(vai->ebo);
|
||||
}
|
||||
} else {
|
||||
gpuStats.numCachedDrawCalls++;
|
||||
glstate.arrayBuffer.bind(vai->vbo);
|
||||
glstate.elementArrayBuffer.bind(vai->ebo);
|
||||
useElements = vai->ebo ? true : false;
|
||||
gpuStats.numCachedVertsDrawn += vai->numVerts;
|
||||
gstate_c.vertexFullAlpha = vai->flags & VAI_FLAG_VERTEXFULLALPHA;
|
||||
}
|
||||
vbo = vai->vbo;
|
||||
ebo = vai->ebo;
|
||||
vertexBuffer = vai->vbo;
|
||||
indexBuffer = vai->ebo;
|
||||
vertexCount = vai->numVerts;
|
||||
prim = static_cast<GEPrimitiveType>(vai->prim);
|
||||
break;
|
||||
@@ -573,10 +451,8 @@ void DrawEngineGLES::DoFlush() {
|
||||
}
|
||||
gpuStats.numCachedDrawCalls++;
|
||||
gpuStats.numCachedVertsDrawn += vai->numVerts;
|
||||
vbo = vai->vbo;
|
||||
ebo = vai->ebo;
|
||||
glstate.arrayBuffer.bind(vbo);
|
||||
glstate.elementArrayBuffer.bind(ebo);
|
||||
vertexBuffer = vai->vbo;
|
||||
indexBuffer = vai->ebo;
|
||||
vertexCount = vai->numVerts;
|
||||
prim = static_cast<GEPrimitiveType>(vai->prim);
|
||||
|
||||
@@ -597,7 +473,15 @@ void DrawEngineGLES::DoFlush() {
|
||||
|
||||
vai->lastFrame = gpuStats.numFlips;
|
||||
} else {
|
||||
DecodeVerts(decoded);
|
||||
if (lastVType_ & GE_VTYPE_WEIGHT_MASK) {
|
||||
// If skinning, we've already predecoded into "decoded". So push that content.
|
||||
size_t size = decodedVerts_ * dec_->GetDecVtxFmt().stride;
|
||||
u8 *dest = (u8 *)frameData.pushVertex->Push(size, &vertexBufferOffset, &vertexBuffer);
|
||||
memcpy(dest, decoded, size);
|
||||
} else {
|
||||
// Decode directly into the pushbuffer
|
||||
DecodeVertsToPushBuffer(frameData.pushVertex, &vertexBufferOffset, &vertexBuffer);
|
||||
}
|
||||
|
||||
rotateVBO:
|
||||
gpuStats.numUncachedVertsDrawn += indexGen.VertexCount();
|
||||
@@ -606,9 +490,6 @@ rotateVBO:
|
||||
if (!useElements && indexGen.PureCount()) {
|
||||
vertexCount = indexGen.PureCount();
|
||||
}
|
||||
glstate.arrayBuffer.unbind();
|
||||
glstate.elementArrayBuffer.unbind();
|
||||
|
||||
prim = indexGen.Prim();
|
||||
}
|
||||
|
||||
@@ -620,26 +501,27 @@ rotateVBO:
|
||||
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255);
|
||||
}
|
||||
|
||||
ApplyDrawStateLate();
|
||||
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_VAO) && vbo == 0) {
|
||||
vbo = BindBuffer(decoded, dec_->GetDecVtxFmt().stride * indexGen.MaxIndex());
|
||||
if (useElements) {
|
||||
ebo = BindElementBuffer(decIndex, sizeof(short) * indexGen.VertexCount());
|
||||
}
|
||||
}
|
||||
if (textureNeedsApply)
|
||||
textureCache_->ApplyTexture();
|
||||
|
||||
// Need to ApplyDrawState after ApplyTexture because depal can launch a render pass and that wrecks the state.
|
||||
ApplyDrawState(prim);
|
||||
ApplyDrawStateLate(false, 0);
|
||||
|
||||
LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, lastVType_, prim);
|
||||
SetupDecFmtForDraw(program, dec_->GetDecVtxFmt(), vbo ? 0 : decoded);
|
||||
|
||||
GLRInputLayout *inputLayout = SetupDecFmtForDraw(program, dec_->GetDecVtxFmt());
|
||||
render_->BindVertexBuffer(inputLayout, vertexBuffer, vertexBufferOffset);
|
||||
if (useElements) {
|
||||
if (!indexBuffer) {
|
||||
indexBufferOffset = (uint32_t)frameData.pushIndex->Push(decIndex, sizeof(uint16_t) * indexGen.VertexCount(), &indexBuffer);
|
||||
render_->BindIndexBuffer(indexBuffer);
|
||||
}
|
||||
if (gstate_c.bezier || gstate_c.spline)
|
||||
// Instanced rendering for instanced tessellation
|
||||
glDrawElementsInstanced(glprim[prim], vertexCount, GL_UNSIGNED_SHORT, ebo ? 0 : (GLvoid*)decIndex, numPatches);
|
||||
render_->DrawIndexed(glprim[prim], vertexCount, GL_UNSIGNED_SHORT, (GLvoid*)(intptr_t)indexBufferOffset, numPatches);
|
||||
else
|
||||
glDrawElements(glprim[prim], vertexCount, GL_UNSIGNED_SHORT, ebo ? 0 : (GLvoid*)decIndex);
|
||||
render_->DrawIndexed(glprim[prim], vertexCount, GL_UNSIGNED_SHORT, (GLvoid*)(intptr_t)indexBufferOffset);
|
||||
} else {
|
||||
glDrawArrays(glprim[prim], 0, vertexCount);
|
||||
render_->Draw(glprim[prim], 0, vertexCount);
|
||||
}
|
||||
} else {
|
||||
DecodeVerts(decoded);
|
||||
@@ -683,40 +565,30 @@ rotateVBO:
|
||||
prim, vertexCount,
|
||||
dec_->VertexType(), inds, GE_VTYPE_IDX_16BIT, dec_->GetDecVtxFmt(),
|
||||
maxIndex, drawBuffer, numTrans, drawIndexed, ¶ms, &result);
|
||||
ApplyDrawStateLate();
|
||||
|
||||
if (textureNeedsApply)
|
||||
textureCache_->ApplyTexture();
|
||||
|
||||
ApplyDrawState(prim);
|
||||
ApplyDrawStateLate(result.setStencil, result.stencilValue);
|
||||
|
||||
LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, lastVType_, prim);
|
||||
|
||||
if (result.action == SW_DRAW_PRIMITIVES) {
|
||||
if (result.setStencil) {
|
||||
glstate.stencilFunc.set(GL_ALWAYS, result.stencilValue, 255);
|
||||
}
|
||||
const int vertexSize = sizeof(transformed[0]);
|
||||
|
||||
bool doTextureProjection = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX;
|
||||
|
||||
const uint8_t *bufferStart = (const uint8_t *)drawBuffer;
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_VAO)) {
|
||||
bufferStart = 0;
|
||||
BindBuffer(drawBuffer, vertexSize * maxIndex);
|
||||
if (drawIndexed) {
|
||||
BindElementBuffer(inds, sizeof(short) * numTrans);
|
||||
inds = 0;
|
||||
}
|
||||
} else {
|
||||
glstate.arrayBuffer.unbind();
|
||||
glstate.elementArrayBuffer.unbind();
|
||||
}
|
||||
|
||||
glVertexAttribPointer(ATTR_POSITION, 4, GL_FLOAT, GL_FALSE, vertexSize, bufferStart);
|
||||
int attrMask = program->attrMask;
|
||||
if (attrMask & (1 << ATTR_TEXCOORD)) glVertexAttribPointer(ATTR_TEXCOORD, doTextureProjection ? 3 : 2, GL_FLOAT, GL_FALSE, vertexSize, bufferStart + offsetof(TransformedVertex, u));
|
||||
if (attrMask & (1 << ATTR_COLOR0)) glVertexAttribPointer(ATTR_COLOR0, 4, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, bufferStart + offsetof(TransformedVertex, color0));
|
||||
if (attrMask & (1 << ATTR_COLOR1)) glVertexAttribPointer(ATTR_COLOR1, 3, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, bufferStart + offsetof(TransformedVertex, color1));
|
||||
if (drawIndexed) {
|
||||
glDrawElements(glprim[prim], numTrans, GL_UNSIGNED_SHORT, inds);
|
||||
vertexBufferOffset = (uint32_t)frameData.pushVertex->Push(drawBuffer, maxIndex * sizeof(TransformedVertex), &vertexBuffer);
|
||||
indexBufferOffset = (uint32_t)frameData.pushIndex->Push(inds, sizeof(uint16_t) * numTrans, &indexBuffer);
|
||||
render_->BindVertexBuffer(softwareInputLayout_, vertexBuffer, vertexBufferOffset);
|
||||
render_->BindIndexBuffer(indexBuffer);
|
||||
render_->DrawIndexed(glprim[prim], numTrans, GL_UNSIGNED_SHORT, (void *)(intptr_t)indexBufferOffset);
|
||||
} else {
|
||||
glDrawArrays(glprim[prim], 0, numTrans);
|
||||
vertexBufferOffset = (uint32_t)frameData.pushVertex->Push(drawBuffer, numTrans * sizeof(TransformedVertex), &vertexBuffer);
|
||||
render_->BindVertexBuffer(softwareInputLayout_, vertexBuffer, vertexBufferOffset);
|
||||
render_->Draw(glprim[prim], 0, numTrans);
|
||||
}
|
||||
} else if (result.action == SW_CLEAR) {
|
||||
u32 clearColor = result.color;
|
||||
@@ -735,37 +607,26 @@ rotateVBO:
|
||||
framebufferManager_->SetDepthUpdated();
|
||||
}
|
||||
|
||||
// Note that scissor may still apply while clearing. Turn off other tests for the clear.
|
||||
glstate.stencilTest.disable();
|
||||
glstate.stencilMask.set(0xFF);
|
||||
glstate.depthTest.disable();
|
||||
|
||||
GLbitfield target = 0;
|
||||
// Without this, we will clear RGB when clearing stencil, which breaks games.
|
||||
uint8_t rgbaMask = (colorMask ? 7 : 0) | (alphaMask ? 8 : 0);
|
||||
if (colorMask || alphaMask) target |= GL_COLOR_BUFFER_BIT;
|
||||
if (alphaMask) target |= GL_STENCIL_BUFFER_BIT;
|
||||
if (depthMask) target |= GL_DEPTH_BUFFER_BIT;
|
||||
|
||||
glstate.colorMask.set(colorMask, colorMask, colorMask, alphaMask);
|
||||
glClearColor(col[0], col[1], col[2], col[3]);
|
||||
#ifdef USING_GLES2
|
||||
glClearDepthf(clearDepth);
|
||||
#else
|
||||
glClearDepth(clearDepth);
|
||||
#endif
|
||||
// Stencil takes alpha.
|
||||
glClearStencil(clearColor >> 24);
|
||||
glClear(target);
|
||||
framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason);
|
||||
|
||||
int scissorX1 = gstate.getScissorX1();
|
||||
int scissorY1 = gstate.getScissorY1();
|
||||
int scissorX2 = gstate.getScissorX2() + 1;
|
||||
int scissorY2 = gstate.getScissorY2() + 1;
|
||||
|
||||
render_->Clear(clearColor, clearDepth, clearColor >> 24, target, rgbaMask, vpAndScissor.scissorX, vpAndScissor.scissorY, vpAndScissor.scissorW, vpAndScissor.scissorH);
|
||||
framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason);
|
||||
framebufferManager_->SetSafeSize(scissorX2, scissorY2);
|
||||
|
||||
if (g_Config.bBlockTransferGPU && (gstate_c.featureFlags & GPU_USE_CLEAR_RAM_HACK) && colorMask && (alphaMask || gstate.FrameBufFormat() == GE_FORMAT_565)) {
|
||||
framebufferManager_->ApplyClearToMemory(scissorX1, scissorY1, scissorX2, scissorY2, clearColor);
|
||||
}
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE); // Make sure the color mask gets re-applied.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -791,104 +652,6 @@ rotateVBO:
|
||||
#ifndef MOBILE_DEVICE
|
||||
host->GPUNotifyDraw();
|
||||
#endif
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
GLuint DrawEngineGLES::BindBuffer(const void *p, size_t sz) {
|
||||
// Get a new buffer each time we need one.
|
||||
GLuint buf = AllocateBuffer(sz);
|
||||
glstate.arrayBuffer.bind(buf);
|
||||
|
||||
// These aren't used more than once per frame, so let's use GL_STREAM_DRAW.
|
||||
glBufferData(GL_ARRAY_BUFFER, sz, p, GL_STREAM_DRAW);
|
||||
buffersThisFrame_.push_back(buf);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
GLuint DrawEngineGLES::BindBuffer(const void *p1, size_t sz1, const void *p2, size_t sz2) {
|
||||
GLuint buf = AllocateBuffer(sz1 + sz2);
|
||||
glstate.arrayBuffer.bind(buf);
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, sz1 + sz2, nullptr, GL_STREAM_DRAW);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, sz1, p1);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, sz1, sz2, p2);
|
||||
buffersThisFrame_.push_back(buf);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
GLuint DrawEngineGLES::BindElementBuffer(const void *p, size_t sz) {
|
||||
GLuint buf = AllocateBuffer(sz);
|
||||
glstate.elementArrayBuffer.bind(buf);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sz, p, GL_STREAM_DRAW);
|
||||
buffersThisFrame_.push_back(buf);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void DrawEngineGLES::DecimateBuffers() {
|
||||
for (GLuint buf : buffersThisFrame_) {
|
||||
FreeBuffer(buf);
|
||||
}
|
||||
buffersThisFrame_.clear();
|
||||
|
||||
if (--bufferDecimationCounter_ <= 0) {
|
||||
bufferDecimationCounter_ = VERTEXCACHE_DECIMATION_INTERVAL;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's not keep too many around, will eat up memory.
|
||||
// First check if there's any to free, and only check if it seems somewhat full.
|
||||
bool hasOld = false;
|
||||
if (bufferNameCacheSize_ > VERTEXCACHE_NAME_CACHE_FULL_BYTES) {
|
||||
for (GLuint buf : bufferNameCache_) {
|
||||
const BufferNameInfo &info = bufferNameInfo_[buf];
|
||||
const int age = gpuStats.numFlips - info.lastFrame;
|
||||
if (!info.used && age > VERTEXCACHE_NAME_CACHE_MAX_AGE) {
|
||||
hasOld = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasOld) {
|
||||
// Okay, it is. Let's rebuild the array.
|
||||
std::vector<GLuint> toFree;
|
||||
std::vector<GLuint> toKeep;
|
||||
|
||||
toKeep.reserve(bufferNameCache_.size());
|
||||
|
||||
for (size_t i = 0, n = bufferNameCache_.size(); i < n; ++i) {
|
||||
const GLuint buf = bufferNameCache_[i];
|
||||
const BufferNameInfo &info = bufferNameInfo_[buf];
|
||||
const int age = gpuStats.numFlips - info.lastFrame;
|
||||
if (!info.used && age > VERTEXCACHE_NAME_CACHE_MAX_AGE) {
|
||||
toFree.push_back(buf);
|
||||
bufferNameCacheSize_ -= bufferNameInfo_[buf].sz;
|
||||
bufferNameInfo_.erase(buf);
|
||||
|
||||
// If we've removed all we want to this round, keep the rest and abort.
|
||||
if (toFree.size() >= VERTEXCACHE_NAME_DECIMATION_MAX && i + 1 < bufferNameCache_.size()) {
|
||||
toKeep.insert(toKeep.end(), bufferNameCache_.begin() + i + 1, bufferNameCache_.end());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
toKeep.push_back(buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (!toFree.empty()) {
|
||||
bufferNameCache_ = toKeep;
|
||||
// TODO: Rebuild?
|
||||
freeSizedBuffers_.clear();
|
||||
|
||||
glstate.arrayBuffer.unbind();
|
||||
glstate.elementArrayBuffer.unbind();
|
||||
glDeleteBuffers((GLsizei)toFree.size(), &toFree[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawEngineGLES::IsCodePtrVertexDecoder(const u8 *ptr) const {
|
||||
@@ -896,100 +659,45 @@ bool DrawEngineGLES::IsCodePtrVertexDecoder(const u8 *ptr) const {
|
||||
}
|
||||
|
||||
void DrawEngineGLES::TessellationDataTransferGLES::SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) {
|
||||
#ifndef USING_GLES2
|
||||
if (isAllowTexture1D_) {
|
||||
// Position
|
||||
glActiveTexture(GL_TEXTURE4);
|
||||
glBindTexture(GL_TEXTURE_1D, data_tex[0]);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
if (prevSize < size) {
|
||||
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, size, 0, GL_RGBA, GL_FLOAT, (GLfloat*)pos);
|
||||
prevSize = size;
|
||||
} else {
|
||||
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, size, GL_RGBA, GL_FLOAT, (GLfloat*)pos);
|
||||
}
|
||||
// Removed the 1D texture support, it's unlikely to be relevant for performance.
|
||||
if (data_tex[0])
|
||||
renderManager_->DeleteTexture(data_tex[0]);
|
||||
uint8_t *pos_data = new uint8_t[size * sizeof(float) * 4];
|
||||
memcpy(pos_data, pos, size * sizeof(float) * 4);
|
||||
data_tex[0] = renderManager_->CreateTexture(GL_TEXTURE_2D);
|
||||
renderManager_->TextureImage(data_tex[0], 0, size, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT, pos_data, GLRAllocType::NEW, false);
|
||||
renderManager_->FinalizeTexture(data_tex[0], 0, false);
|
||||
renderManager_->BindTexture(4, data_tex[0]);
|
||||
|
||||
// Texcoords
|
||||
if (hasTexCoords) {
|
||||
glActiveTexture(GL_TEXTURE5);
|
||||
glBindTexture(GL_TEXTURE_1D, data_tex[1]);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
if (prevSizeTex < size) {
|
||||
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, size, 0, GL_RGBA, GL_FLOAT, (GLfloat*)tex);
|
||||
prevSizeTex = size;
|
||||
} else {
|
||||
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, size, GL_RGBA, GL_FLOAT, (GLfloat*)tex);
|
||||
}
|
||||
}
|
||||
// Texcoords
|
||||
if (hasTexCoords) {
|
||||
if (data_tex[1])
|
||||
renderManager_->DeleteTexture(data_tex[1]);
|
||||
uint8_t *tex_data = new uint8_t[size * sizeof(float) * 4];
|
||||
memcpy(tex_data, pos, size * sizeof(float) * 4);
|
||||
data_tex[1] = renderManager_->CreateTexture(GL_TEXTURE_2D);
|
||||
renderManager_->TextureImage(data_tex[1], 0, size, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT, tex_data, GLRAllocType::NEW, false);
|
||||
renderManager_->FinalizeTexture(data_tex[1], 0, false);
|
||||
renderManager_->BindTexture(5, data_tex[1]);
|
||||
}
|
||||
|
||||
// Color
|
||||
glActiveTexture(GL_TEXTURE6);
|
||||
glBindTexture(GL_TEXTURE_1D, data_tex[2]);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
int sizeColor = hasColor ? size : 1;
|
||||
if (prevSizeCol < sizeColor) {
|
||||
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, sizeColor, 0, GL_RGBA, GL_FLOAT, (GLfloat*)col);
|
||||
prevSizeCol = sizeColor;
|
||||
} else {
|
||||
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, sizeColor, GL_RGBA, GL_FLOAT, (GLfloat*)col);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// Position
|
||||
glActiveTexture(GL_TEXTURE4);
|
||||
glBindTexture(GL_TEXTURE_2D, data_tex[0]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
if (prevSize < size) {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, size, 1, 0, GL_RGBA, GL_FLOAT, (GLfloat*)pos);
|
||||
prevSize = size;
|
||||
} else {
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, 1, GL_RGBA, GL_FLOAT, (GLfloat*)pos);
|
||||
}
|
||||
if (data_tex[2])
|
||||
renderManager_->DeleteTexture(data_tex[2]);
|
||||
data_tex[2] = renderManager_->CreateTexture(GL_TEXTURE_2D);
|
||||
int sizeColor = hasColor ? size : 1;
|
||||
uint8_t *col_data = new uint8_t[sizeColor * sizeof(float) * 4];
|
||||
memcpy(col_data, col, sizeColor * sizeof(float) * 4);
|
||||
|
||||
// Texcoords
|
||||
if (hasTexCoords) {
|
||||
glActiveTexture(GL_TEXTURE5);
|
||||
glBindTexture(GL_TEXTURE_2D, data_tex[1]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
if (prevSizeTex < size) {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, size, 1, 0, GL_RGBA, GL_FLOAT, (GLfloat*)tex);
|
||||
prevSizeTex = size;
|
||||
} else {
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, 1, GL_RGBA, GL_FLOAT, (GLfloat*)tex);
|
||||
}
|
||||
}
|
||||
renderManager_->TextureImage(data_tex[2], 0, sizeColor, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT, col_data, GLRAllocType::NEW, false);
|
||||
renderManager_->FinalizeTexture(data_tex[2], 0, false);
|
||||
renderManager_->BindTexture(6, data_tex[2]);
|
||||
}
|
||||
|
||||
// Color
|
||||
glActiveTexture(GL_TEXTURE6);
|
||||
glBindTexture(GL_TEXTURE_2D, data_tex[2]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
int sizeColor = hasColor ? size : 1;
|
||||
if (prevSizeCol < sizeColor) {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, sizeColor, 1, 0, GL_RGBA, GL_FLOAT, (GLfloat*)col);
|
||||
prevSizeCol = sizeColor;
|
||||
} else {
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sizeColor, 1, GL_RGBA, GL_FLOAT, (GLfloat*)col);
|
||||
void DrawEngineGLES::TessellationDataTransferGLES::EndFrame() {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (data_tex[i]) {
|
||||
renderManager_->DeleteTexture(data_tex[i]);
|
||||
data_tex[i] = nullptr;
|
||||
}
|
||||
}
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
+42
-38
@@ -28,6 +28,7 @@
|
||||
#include "GPU/Common/GPUStateUtils.h"
|
||||
#include "GPU/GLES/FragmentShaderGeneratorGLES.h"
|
||||
#include "gfx/gl_common.h"
|
||||
#include "thin3d/GLRenderManager.h"
|
||||
|
||||
class LinkedShader;
|
||||
class ShaderManagerGLES;
|
||||
@@ -60,8 +61,8 @@ class VertexArrayInfo {
|
||||
public:
|
||||
VertexArrayInfo() {
|
||||
status = VAI_NEW;
|
||||
vbo = 0;
|
||||
ebo = 0;
|
||||
vbo = nullptr;
|
||||
ebo = nullptr;
|
||||
prim = GE_PRIM_INVALID;
|
||||
numDraws = 0;
|
||||
numFrames = 0;
|
||||
@@ -81,8 +82,8 @@ public:
|
||||
ReliableHashType hash;
|
||||
u32 minihash;
|
||||
|
||||
u32 vbo;
|
||||
u32 ebo;
|
||||
GLRBuffer *vbo;
|
||||
GLRBuffer *ebo;
|
||||
|
||||
// Precalculated parameter for drawRangeElements
|
||||
u16 numVerts;
|
||||
@@ -101,11 +102,9 @@ public:
|
||||
// Handles transform, lighting and drawing.
|
||||
class DrawEngineGLES : public DrawEngineCommon {
|
||||
public:
|
||||
DrawEngineGLES();
|
||||
DrawEngineGLES(Draw::DrawContext *draw);
|
||||
virtual ~DrawEngineGLES();
|
||||
|
||||
void SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead);
|
||||
|
||||
void SetShaderManager(ShaderManagerGLES *shaderManager) {
|
||||
shaderManager_ = shaderManager;
|
||||
}
|
||||
@@ -118,7 +117,6 @@ public:
|
||||
void SetFragmentTestCache(FragmentTestCacheGLES *testCache) {
|
||||
fragmentTestCache_ = testCache;
|
||||
}
|
||||
void RestoreVAO();
|
||||
|
||||
void DeviceLost();
|
||||
void DeviceRestore();
|
||||
@@ -126,6 +124,10 @@ public:
|
||||
void ClearTrackedVertexArrays() override;
|
||||
void DecimateTrackedVertexArrays();
|
||||
|
||||
void BeginFrame();
|
||||
void EndFrame();
|
||||
|
||||
|
||||
// So that this can be inlined
|
||||
void Flush() {
|
||||
if (!numDrawCalls)
|
||||
@@ -136,20 +138,21 @@ public:
|
||||
void FinishDeferred() {
|
||||
if (!numDrawCalls)
|
||||
return;
|
||||
DecodeVerts(decoded);
|
||||
DoFlush();
|
||||
}
|
||||
|
||||
bool IsCodePtrVertexDecoder(const u8 *ptr) const;
|
||||
|
||||
void DispatchFlush() override { Flush(); }
|
||||
void DispatchSubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) override {
|
||||
SubmitPrim(verts, inds, prim, vertexCount, vertType, bytesRead);
|
||||
|
||||
GLPushBuffer *GetPushVertexBuffer() {
|
||||
return frameData_[render_->GetCurFrame()].pushVertex;
|
||||
}
|
||||
GLPushBuffer *GetPushIndexBuffer() {
|
||||
return frameData_[render_->GetCurFrame()].pushIndex;
|
||||
}
|
||||
|
||||
GLuint BindBuffer(const void *p, size_t sz);
|
||||
GLuint BindBuffer(const void *p1, size_t sz1, const void *p2, size_t sz2);
|
||||
GLuint BindElementBuffer(const void *p, size_t sz);
|
||||
void DecimateBuffers();
|
||||
void ClearInputLayoutMap();
|
||||
|
||||
private:
|
||||
void InitDeviceObjects();
|
||||
@@ -157,53 +160,54 @@ private:
|
||||
|
||||
void DoFlush();
|
||||
void ApplyDrawState(int prim);
|
||||
void ApplyDrawStateLate();
|
||||
void ApplyDrawStateLate(bool setStencil, int stencilValue);
|
||||
void ResetShaderBlending();
|
||||
|
||||
GLuint AllocateBuffer(size_t sz);
|
||||
void FreeBuffer(GLuint buf);
|
||||
GLRInputLayout *SetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt);
|
||||
|
||||
void DecodeVertsToPushBuffer(GLPushBuffer *push, uint32_t *bindOffset, GLRBuffer **buf);
|
||||
|
||||
void FreeVertexArray(VertexArrayInfo *vai);
|
||||
|
||||
void MarkUnreliable(VertexArrayInfo *vai);
|
||||
|
||||
struct FrameData {
|
||||
GLPushBuffer *pushVertex;
|
||||
GLPushBuffer *pushIndex;
|
||||
};
|
||||
FrameData frameData_[GLRenderManager::MAX_INFLIGHT_FRAMES];
|
||||
|
||||
PrehashMap<VertexArrayInfo *, nullptr> vai_;
|
||||
|
||||
// Vertex buffer objects
|
||||
// Element buffer objects
|
||||
struct BufferNameInfo {
|
||||
BufferNameInfo() : sz(0), used(false), lastFrame(0) {}
|
||||
DenseHashMap<uint32_t, GLRInputLayout *, nullptr> inputLayoutMap_;
|
||||
|
||||
size_t sz;
|
||||
bool used;
|
||||
int lastFrame;
|
||||
};
|
||||
std::vector<GLuint> bufferNameCache_;
|
||||
std::multimap<size_t, GLuint> freeSizedBuffers_;
|
||||
std::unordered_map<GLuint, BufferNameInfo> bufferNameInfo_;
|
||||
std::vector<GLuint> buffersThisFrame_;
|
||||
size_t bufferNameCacheSize_ = 0;
|
||||
GLuint sharedVao_ = 0;
|
||||
GLRInputLayout *softwareInputLayout_ = nullptr;
|
||||
GLRenderManager *render_;
|
||||
|
||||
// Other
|
||||
ShaderManagerGLES *shaderManager_ = nullptr;
|
||||
TextureCacheGLES *textureCache_ = nullptr;
|
||||
FramebufferManagerGLES *framebufferManager_ = nullptr;
|
||||
FragmentTestCacheGLES *fragmentTestCache_ = nullptr;
|
||||
Draw::DrawContext *draw_;
|
||||
|
||||
// Need to preserve the scissor for use when clearing.
|
||||
ViewportAndScissor vpAndScissor;
|
||||
|
||||
int bufferDecimationCounter_ = 0;
|
||||
|
||||
// Hardware tessellation
|
||||
class TessellationDataTransferGLES : public TessellationDataTransfer {
|
||||
private:
|
||||
int data_tex[3];
|
||||
bool isAllowTexture1D_;
|
||||
GLRTexture *data_tex[3]{};
|
||||
GLRenderManager *renderManager_;
|
||||
public:
|
||||
TessellationDataTransferGLES(bool isAllowTexture1D) : TessellationDataTransfer(), data_tex(), isAllowTexture1D_(isAllowTexture1D) {
|
||||
glGenTextures(3, (GLuint*)data_tex);
|
||||
}
|
||||
TessellationDataTransferGLES(GLRenderManager *renderManager)
|
||||
: renderManager_(renderManager) { }
|
||||
~TessellationDataTransferGLES() {
|
||||
glDeleteTextures(3, (GLuint*)data_tex);
|
||||
EndFrame();
|
||||
}
|
||||
void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) override;
|
||||
void EndFrame() override; // Queues textures for deletion.
|
||||
};
|
||||
};
|
||||
@@ -15,6 +15,7 @@
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "thin3d/thin3d.h"
|
||||
#include "gfx/gl_debug_log.h"
|
||||
#include "Core/Config.h"
|
||||
#include "GPU/GLES/FragmentTestCacheGLES.h"
|
||||
@@ -26,16 +27,15 @@
|
||||
static const int FRAGTEST_TEXTURE_OLD_AGE = 307;
|
||||
static const int FRAGTEST_DECIMATION_INTERVAL = 113;
|
||||
|
||||
FragmentTestCacheGLES::FragmentTestCacheGLES() : textureCache_(NULL), lastTexture_(0), decimationCounter_(0) {
|
||||
scratchpad_ = new u8[256 * 4];
|
||||
FragmentTestCacheGLES::FragmentTestCacheGLES(Draw::DrawContext *draw) {
|
||||
render_ = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
}
|
||||
|
||||
FragmentTestCacheGLES::~FragmentTestCacheGLES() {
|
||||
Clear();
|
||||
delete [] scratchpad_;
|
||||
}
|
||||
|
||||
void FragmentTestCacheGLES::BindTestTexture(GLenum unit) {
|
||||
void FragmentTestCacheGLES::BindTestTexture(int slot) {
|
||||
if (!g_Config.bFragmentTestCache) {
|
||||
return;
|
||||
}
|
||||
@@ -51,17 +51,12 @@ void FragmentTestCacheGLES::BindTestTexture(GLenum unit) {
|
||||
const auto cached = cache_.find(id);
|
||||
if (cached != cache_.end()) {
|
||||
cached->second.lastFrame = gpuStats.numFlips;
|
||||
GLuint tex = cached->second.texture;
|
||||
GLRTexture *tex = cached->second.texture;
|
||||
if (tex == lastTexture_) {
|
||||
// Already bound, hurray.
|
||||
return;
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
glActiveTexture(unit);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
// Always return to the default.
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
render_->BindTexture(slot, tex);
|
||||
lastTexture_ = tex;
|
||||
return;
|
||||
}
|
||||
@@ -79,13 +74,9 @@ void FragmentTestCacheGLES::BindTestTexture(GLenum unit) {
|
||||
const GEComparison funcs[4] = {gstate.getColorTestFunction(), gstate.getColorTestFunction(), gstate.getColorTestFunction(), gstate.getAlphaTestFunction()};
|
||||
const bool valid[4] = {gstate.isColorTestEnabled(), gstate.isColorTestEnabled(), gstate.isColorTestEnabled(), gstate.isAlphaTestEnabled()};
|
||||
|
||||
glActiveTexture(unit);
|
||||
// This will necessarily bind the texture.
|
||||
const GLuint tex = CreateTestTexture(funcs, refs, masks, valid);
|
||||
// Always return to the default.
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
GLRTexture *tex = CreateTestTexture(funcs, refs, masks, valid);
|
||||
lastTexture_ = tex;
|
||||
|
||||
render_->BindTexture(slot, tex);
|
||||
FragmentTestTexture item;
|
||||
item.lastFrame = gpuStats.numFlips;
|
||||
item.texture = tex;
|
||||
@@ -106,7 +97,8 @@ FragmentTestID FragmentTestCacheGLES::GenerateTestID() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
GLuint FragmentTestCacheGLES::CreateTestTexture(const GEComparison funcs[4], const u8 refs[4], const u8 masks[4], const bool valid[4]) {
|
||||
GLRTexture *FragmentTestCacheGLES::CreateTestTexture(const GEComparison funcs[4], const u8 refs[4], const u8 masks[4], const bool valid[4]) {
|
||||
u8 *data = new u8[256 * 4];
|
||||
// TODO: Might it be better to use GL_ALPHA for simple textures?
|
||||
// TODO: Experiment with 4-bit/etc. textures.
|
||||
|
||||
@@ -142,26 +134,19 @@ GLuint FragmentTestCacheGLES::CreateTestTexture(const GEComparison funcs[4], con
|
||||
break;
|
||||
}
|
||||
}
|
||||
scratchpad_[color * 4 + i] = res ? 0xFF : 0;
|
||||
data[color * 4 + i] = res ? 0xFF : 0;
|
||||
}
|
||||
}
|
||||
|
||||
GLuint tex = textureCache_->AllocTextureName();
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, scratchpad_);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
GLRTexture *tex = render_->CreateTexture(GL_TEXTURE_2D);
|
||||
render_->TextureImage(tex, 0, 256, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
return tex;
|
||||
}
|
||||
|
||||
void FragmentTestCacheGLES::Clear(bool deleteThem) {
|
||||
if (deleteThem) {
|
||||
for (auto tex = cache_.begin(); tex != cache_.end(); ++tex) {
|
||||
glDeleteTextures(1, &tex->second.texture);
|
||||
render_->DeleteTexture(tex->second.texture);
|
||||
}
|
||||
}
|
||||
cache_.clear();
|
||||
@@ -172,7 +157,7 @@ void FragmentTestCacheGLES::Decimate() {
|
||||
if (--decimationCounter_ <= 0) {
|
||||
for (auto tex = cache_.begin(); tex != cache_.end(); ) {
|
||||
if (tex->second.lastFrame + FRAGTEST_TEXTURE_OLD_AGE < gpuStats.numFlips) {
|
||||
glDeleteTextures(1, &tex->second.texture);
|
||||
render_->DeleteTexture(tex->second.texture);
|
||||
cache_.erase(tex++);
|
||||
} else {
|
||||
++tex;
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include <map>
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "ext/native/gfx/GLStateCache.h"
|
||||
#include "GPU/GLES/TextureCacheGLES.h"
|
||||
|
||||
#include "GPU/ge_constants.h"
|
||||
@@ -53,33 +52,33 @@ struct FragmentTestID {
|
||||
};
|
||||
|
||||
struct FragmentTestTexture {
|
||||
GLuint texture;
|
||||
GLRTexture *texture;
|
||||
int lastFrame;
|
||||
};
|
||||
|
||||
class FragmentTestCacheGLES {
|
||||
public:
|
||||
FragmentTestCacheGLES();
|
||||
FragmentTestCacheGLES(Draw::DrawContext *draw);
|
||||
~FragmentTestCacheGLES();
|
||||
|
||||
void SetTextureCache(TextureCacheGLES *tc) {
|
||||
textureCache_ = tc;
|
||||
}
|
||||
|
||||
void BindTestTexture(GLenum unit);
|
||||
void BindTestTexture(int slot);
|
||||
|
||||
void Clear(bool deleteThem = true);
|
||||
void Decimate();
|
||||
|
||||
private:
|
||||
|
||||
GLuint CreateTestTexture(const GEComparison funcs[4], const u8 refs[4], const u8 masks[4], const bool valid[4]);
|
||||
GLRTexture *CreateTestTexture(const GEComparison funcs[4], const u8 refs[4], const u8 masks[4], const bool valid[4]);
|
||||
FragmentTestID GenerateTestID() const;
|
||||
|
||||
GLRenderManager *render_;
|
||||
TextureCacheGLES *textureCache_;
|
||||
|
||||
std::map<FragmentTestID, FragmentTestTexture> cache_;
|
||||
u8 *scratchpad_;
|
||||
GLuint lastTexture_;
|
||||
int decimationCounter_;
|
||||
GLRTexture *lastTexture_ = nullptr;
|
||||
int decimationCounter_ = 0;
|
||||
};
|
||||
+152
-467
@@ -42,7 +42,6 @@
|
||||
#include "GPU/Common/TextureDecoder.h"
|
||||
#include "GPU/Common/FramebufferCommon.h"
|
||||
#include "GPU/Debugger/Stepping.h"
|
||||
#include "ext/native/gfx/GLStateCache.h"
|
||||
#include "GPU/GLES/FramebufferManagerGLES.h"
|
||||
#include "GPU/GLES/TextureCacheGLES.h"
|
||||
#include "GPU/GLES/DrawEngineGLES.h"
|
||||
@@ -81,34 +80,26 @@ const int MAX_PBO = 2;
|
||||
|
||||
void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format);
|
||||
|
||||
void FramebufferManagerGLES::DisableState() {
|
||||
glstate.blend.disable();
|
||||
glstate.cullFace.disable();
|
||||
glstate.depthTest.disable();
|
||||
glstate.scissorTest.disable();
|
||||
glstate.stencilTest.disable();
|
||||
#if !defined(USING_GLES2)
|
||||
glstate.colorLogicOp.disable();
|
||||
#endif
|
||||
glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glstate.stencilMask.set(0xFF);
|
||||
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE);
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::CompileDraw2DProgram() {
|
||||
if (!draw2dprogram_) {
|
||||
std::string errorString;
|
||||
static std::string vs_code, fs_code;
|
||||
vs_code = ApplyGLSLPrelude(basic_vs, GL_VERTEX_SHADER);
|
||||
fs_code = ApplyGLSLPrelude(tex_fs, GL_FRAGMENT_SHADER);
|
||||
draw2dprogram_ = glsl_create_source(vs_code.c_str(), fs_code.c_str(), &errorString);
|
||||
if (!draw2dprogram_) {
|
||||
ERROR_LOG_REPORT(G3D, "Failed to compile draw2dprogram! This shouldn't happen.\n%s", errorString.c_str());
|
||||
} else {
|
||||
glsl_bind(draw2dprogram_);
|
||||
glUniform1i(draw2dprogram_->sampler0, 0);
|
||||
}
|
||||
std::vector<GLRShader *> shaders;
|
||||
shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, vs_code, "draw2d"));
|
||||
shaders.push_back(render_->CreateShader(GL_FRAGMENT_SHADER, fs_code, "draw2d"));
|
||||
|
||||
std::vector<GLRProgram::UniformLocQuery> queries;
|
||||
queries.push_back({ &u_draw2d_tex, "u_tex" });
|
||||
std::vector<GLRProgram::Initializer> initializers;
|
||||
initializers.push_back({ &u_draw2d_tex, 0 });
|
||||
std::vector<GLRProgram::Semantic> semantics;
|
||||
semantics.push_back({ 0, "a_position" });
|
||||
semantics.push_back({ 1, "a_texcoord0" });
|
||||
draw2dprogram_ = render_->CreateProgram(shaders, semantics, queries, initializers, false);
|
||||
for (auto shader : shaders)
|
||||
render_->DeleteShader(shader);
|
||||
CompilePostShader();
|
||||
}
|
||||
}
|
||||
@@ -155,7 +146,27 @@ void FramebufferManagerGLES::CompilePostShader() {
|
||||
}
|
||||
|
||||
if (!translationFailed) {
|
||||
postShaderProgram_ = glsl_create_source(vshader.c_str(), fshader.c_str(), &errorString);
|
||||
SetNumExtraFBOs(1);
|
||||
|
||||
std::vector<GLRShader *> shaders;
|
||||
shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, vshader, "postshader"));
|
||||
shaders.push_back(render_->CreateShader(GL_FRAGMENT_SHADER, fshader, "postshader"));
|
||||
std::vector<GLRProgram::UniformLocQuery> queries;
|
||||
queries.push_back({ &u_postShaderTex, "tex" });
|
||||
queries.push_back({ &deltaLoc_, "u_texelDelta" });
|
||||
queries.push_back({ &pixelDeltaLoc_, "u_pixelDelta" });
|
||||
queries.push_back({ &timeLoc_, "u_time" });
|
||||
queries.push_back({ &videoLoc_, "u_video" });
|
||||
|
||||
std::vector<GLRProgram::Initializer> inits;
|
||||
inits.push_back({ &u_postShaderTex, 0, 0 });
|
||||
std::vector<GLRProgram::Semantic> semantics;
|
||||
semantics.push_back({ 0, "a_position" });
|
||||
semantics.push_back({ 1, "a_texcoord0" });
|
||||
postShaderProgram_ = render_->CreateProgram(shaders, semantics, queries, inits, false);
|
||||
for (auto iter : shaders) {
|
||||
render_->DeleteShader(iter);
|
||||
}
|
||||
} else {
|
||||
ERROR_LOG(FRAMEBUF, "Failed to translate post shader!");
|
||||
}
|
||||
@@ -193,24 +204,16 @@ void FramebufferManagerGLES::CompilePostShader() {
|
||||
}
|
||||
usePostShader_ = false;
|
||||
} else {
|
||||
glsl_bind(postShaderProgram_);
|
||||
glUniform1i(postShaderProgram_->sampler0, 0);
|
||||
SetNumExtraFBOs(1);
|
||||
deltaLoc_ = glsl_uniform_loc(postShaderProgram_, "u_texelDelta");
|
||||
pixelDeltaLoc_ = glsl_uniform_loc(postShaderProgram_, "u_pixelDelta");
|
||||
timeLoc_ = glsl_uniform_loc(postShaderProgram_, "u_time");
|
||||
videoLoc_ = glsl_uniform_loc(postShaderProgram_, "u_video");
|
||||
usePostShader_ = true;
|
||||
}
|
||||
} else {
|
||||
postShaderProgram_ = nullptr;
|
||||
usePostShader_ = false;
|
||||
}
|
||||
glsl_unbind();
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::Bind2DShader() {
|
||||
glsl_bind(draw2dprogram_);
|
||||
render_->BindProgram(draw2dprogram_);
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::BindPostShader(const PostShaderUniforms &uniforms) {
|
||||
@@ -218,35 +221,25 @@ void FramebufferManagerGLES::BindPostShader(const PostShaderUniforms &uniforms)
|
||||
if (!postShaderProgram_) {
|
||||
CompileDraw2DProgram();
|
||||
}
|
||||
|
||||
glsl_bind(postShaderProgram_);
|
||||
render_->BindProgram(postShaderProgram_);
|
||||
if (deltaLoc_ != -1)
|
||||
glUniform2f(deltaLoc_, uniforms.texelDelta[0], uniforms.texelDelta[1]);
|
||||
render_->SetUniformF(&deltaLoc_, 2, uniforms.texelDelta);
|
||||
if (pixelDeltaLoc_ != -1)
|
||||
glUniform2f(pixelDeltaLoc_, uniforms.pixelDelta[0], uniforms.pixelDelta[1]);
|
||||
render_->SetUniformF(&pixelDeltaLoc_, 2, uniforms.pixelDelta);
|
||||
if (timeLoc_ != -1)
|
||||
glUniform4fv(timeLoc_, 1, uniforms.time);
|
||||
render_->SetUniformF(&timeLoc_, 4, uniforms.time);
|
||||
if (videoLoc_ != -1)
|
||||
glUniform1f(videoLoc_, uniforms.video);
|
||||
render_->SetUniformF(&videoLoc_, 1, &uniforms.video);
|
||||
}
|
||||
|
||||
FramebufferManagerGLES::FramebufferManagerGLES(Draw::DrawContext *draw) :
|
||||
FramebufferManagerGLES::FramebufferManagerGLES(Draw::DrawContext *draw, GLRenderManager *render) :
|
||||
FramebufferManagerCommon(draw),
|
||||
drawPixelsTex_(0),
|
||||
drawPixelsTexFormat_(GE_FORMAT_INVALID),
|
||||
convBuf_(nullptr),
|
||||
videoLoc_(-1),
|
||||
timeLoc_(-1),
|
||||
pixelDeltaLoc_(-1),
|
||||
deltaLoc_(-1),
|
||||
textureCacheGL_(nullptr),
|
||||
shaderManagerGL_(nullptr),
|
||||
pixelBufObj_(nullptr),
|
||||
currentPBO_(0)
|
||||
render_(render)
|
||||
{
|
||||
needBackBufferYSwap_ = true;
|
||||
needGLESRebinds_ = true;
|
||||
CreateDeviceObjects();
|
||||
render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::Init() {
|
||||
@@ -273,23 +266,33 @@ void FramebufferManagerGLES::SetDrawEngine(DrawEngineGLES *td) {
|
||||
|
||||
void FramebufferManagerGLES::CreateDeviceObjects() {
|
||||
CompileDraw2DProgram();
|
||||
|
||||
std::vector<GLRInputLayout::Entry> entries;
|
||||
entries.push_back({ 0, 3, GL_FLOAT, GL_FALSE, sizeof(Simple2DVertex), offsetof(Simple2DVertex, pos) });
|
||||
entries.push_back({ 1, 2, GL_FLOAT, GL_FALSE, sizeof(Simple2DVertex), offsetof(Simple2DVertex, uv) });
|
||||
simple2DInputLayout_ = render_->CreateInputLayout(entries);
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::DestroyDeviceObjects() {
|
||||
if (simple2DInputLayout_) {
|
||||
render_->DeleteInputLayout(simple2DInputLayout_);
|
||||
simple2DInputLayout_ = nullptr;
|
||||
}
|
||||
|
||||
if (draw2dprogram_) {
|
||||
glsl_destroy(draw2dprogram_);
|
||||
render_->DeleteProgram(draw2dprogram_);
|
||||
draw2dprogram_ = nullptr;
|
||||
}
|
||||
if (postShaderProgram_) {
|
||||
glsl_destroy(postShaderProgram_);
|
||||
render_->DeleteProgram(postShaderProgram_);
|
||||
postShaderProgram_ = nullptr;
|
||||
}
|
||||
if (drawPixelsTex_) {
|
||||
glDeleteTextures(1, &drawPixelsTex_);
|
||||
render_->DeleteTexture(drawPixelsTex_);
|
||||
drawPixelsTex_ = 0;
|
||||
}
|
||||
if (stencilUploadProgram_) {
|
||||
glsl_destroy(stencilUploadProgram_);
|
||||
render_->DeleteProgram(stencilUploadProgram_);
|
||||
stencilUploadProgram_ = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -297,12 +300,6 @@ void FramebufferManagerGLES::DestroyDeviceObjects() {
|
||||
FramebufferManagerGLES::~FramebufferManagerGLES() {
|
||||
DestroyDeviceObjects();
|
||||
|
||||
if (pixelBufObj_) {
|
||||
for (int i = 0; i < MAX_PBO; i++) {
|
||||
glDeleteBuffers(1, &pixelBufObj_[i].handle);
|
||||
}
|
||||
delete[] pixelBufObj_;
|
||||
}
|
||||
delete [] convBuf_;
|
||||
}
|
||||
|
||||
@@ -317,95 +314,68 @@ void FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferForma
|
||||
}
|
||||
}
|
||||
|
||||
if (drawPixelsTex_ && (drawPixelsTexFormat_ != srcPixelFormat || drawPixelsTexW_ != texWidth || drawPixelsTexH_ != height)) {
|
||||
glDeleteTextures(1, &drawPixelsTex_);
|
||||
drawPixelsTex_ = 0;
|
||||
if (drawPixelsTex_) {
|
||||
render_->DeleteTexture(drawPixelsTex_);
|
||||
}
|
||||
|
||||
if (!drawPixelsTex_) {
|
||||
drawPixelsTex_ = textureCacheGL_->AllocTextureName();
|
||||
drawPixelsTexW_ = texWidth;
|
||||
drawPixelsTexH_ = height;
|
||||
drawPixelsTex_ = render_->CreateTexture(GL_TEXTURE_2D);
|
||||
drawPixelsTexW_ = texWidth;
|
||||
drawPixelsTexH_ = height;
|
||||
|
||||
// Initialize backbuffer texture for DrawPixels
|
||||
glBindTexture(GL_TEXTURE_2D, drawPixelsTex_);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
drawPixelsTexFormat_ = srcPixelFormat;
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, drawPixelsTex_);
|
||||
}
|
||||
drawPixelsTexFormat_ = srcPixelFormat;
|
||||
|
||||
// TODO: We can just change the texture format and flip some bits around instead of this.
|
||||
// Could share code with the texture cache perhaps.
|
||||
bool useConvBuf = false;
|
||||
if (srcPixelFormat != GE_FORMAT_8888 || srcStride != texWidth) {
|
||||
useConvBuf = true;
|
||||
u32 neededSize = texWidth * height * 4;
|
||||
if (!convBuf_ || convBufSize_ < neededSize) {
|
||||
delete [] convBuf_;
|
||||
convBuf_ = new u8[neededSize];
|
||||
convBufSize_ = neededSize;
|
||||
}
|
||||
for (int y = 0; y < height; y++) {
|
||||
switch (srcPixelFormat) {
|
||||
case GE_FORMAT_565:
|
||||
{
|
||||
const u16 *src = (const u16 *)srcPixels + srcStride * y;
|
||||
u8 *dst = convBuf_ + 4 * texWidth * y;
|
||||
ConvertRGBA565ToRGBA8888((u32 *)dst, src, width);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_FORMAT_5551:
|
||||
{
|
||||
const u16 *src = (const u16 *)srcPixels + srcStride * y;
|
||||
u8 *dst = convBuf_ + 4 * texWidth * y;
|
||||
ConvertRGBA5551ToRGBA8888((u32 *)dst, src, width);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_FORMAT_4444:
|
||||
{
|
||||
const u16 *src = (const u16 *)srcPixels + srcStride * y;
|
||||
u8 *dst = convBuf_ + 4 * texWidth * y;
|
||||
ConvertRGBA4444ToRGBA8888((u32 *)dst, src, width);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_FORMAT_8888:
|
||||
{
|
||||
const u8 *src = srcPixels + srcStride * 4 * y;
|
||||
u8 *dst = convBuf_ + 4 * texWidth * y;
|
||||
memcpy(dst, src, 4 * width);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_FORMAT_INVALID:
|
||||
_dbg_assert_msg_(G3D, false, "Invalid pixelFormat passed to DrawPixels().");
|
||||
break;
|
||||
u32 neededSize = texWidth * height * 4;
|
||||
u8 *convBuf = new u8[neededSize];
|
||||
for (int y = 0; y < height; y++) {
|
||||
switch (srcPixelFormat) {
|
||||
case GE_FORMAT_565:
|
||||
{
|
||||
const u16 *src = (const u16 *)srcPixels + srcStride * y;
|
||||
u8 *dst = convBuf + 4 * texWidth * y;
|
||||
ConvertRGBA565ToRGBA8888((u32 *)dst, src, width);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_FORMAT_5551:
|
||||
{
|
||||
const u16 *src = (const u16 *)srcPixels + srcStride * y;
|
||||
u8 *dst = convBuf + 4 * texWidth * y;
|
||||
ConvertRGBA5551ToRGBA8888((u32 *)dst, src, width);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_FORMAT_4444:
|
||||
{
|
||||
const u16 *src = (const u16 *)srcPixels + srcStride * y;
|
||||
u8 *dst = convBuf + 4 * texWidth * y;
|
||||
ConvertRGBA4444ToRGBA8888((u32 *)dst, src, width);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_FORMAT_8888:
|
||||
{
|
||||
const u8 *src = srcPixels + srcStride * 4 * y;
|
||||
u8 *dst = convBuf + 4 * texWidth * y;
|
||||
memcpy(dst, src, 4 * width);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_FORMAT_INVALID:
|
||||
_dbg_assert_msg_(G3D, false, "Invalid pixelFormat passed to DrawPixels().");
|
||||
break;
|
||||
}
|
||||
}
|
||||
render_->TextureImage(drawPixelsTex_, 0, texWidth, height, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, convBuf, GLRAllocType::NEW, false);
|
||||
render_->FinalizeTexture(drawPixelsTex_, 0, false);
|
||||
|
||||
// Try to skip uploading the unnecessary parts.
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_UNPACK_SUBIMAGE) && width != texWidth) {
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, texWidth);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, useConvBuf ? convBuf_ : srcPixels);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
} else {
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texWidth, height, GL_RGBA, GL_UNSIGNED_BYTE, useConvBuf ? convBuf_ : srcPixels);
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
// TODO: Return instead?
|
||||
render_->BindTexture(0, drawPixelsTex_);
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::SetViewport2D(int x, int y, int w, int h) {
|
||||
glstate.viewport.set(x, y, w, h);
|
||||
render_->SetViewport({ (float)x, (float)y, (float)w, (float)h, 0.0f, 1.0f });
|
||||
}
|
||||
|
||||
// x, y, w, h are relative coordinates against destW/destH, which is not very intuitive.
|
||||
@@ -418,7 +388,7 @@ void FramebufferManagerGLES::DrawActiveTexture(float x, float y, float w, float
|
||||
u0,v1,
|
||||
};
|
||||
|
||||
static const GLushort indices[4] = {0,1,3,2};
|
||||
static const GLushort indices[4] = { 0,1,3,2 };
|
||||
|
||||
if (uvRotation != ROTATION_LOCKED_HORIZONTAL) {
|
||||
float temp[8];
|
||||
@@ -437,9 +407,9 @@ void FramebufferManagerGLES::DrawActiveTexture(float x, float y, float w, float
|
||||
|
||||
float pos[12] = {
|
||||
x,y,0,
|
||||
x+w,y,0,
|
||||
x+w,y+h,0,
|
||||
x,y+h,0
|
||||
x + w,y,0,
|
||||
x + w,y + h,0,
|
||||
x,y + h,0
|
||||
};
|
||||
|
||||
float invDestW = 1.0f / (destW * 0.5f);
|
||||
@@ -449,44 +419,37 @@ void FramebufferManagerGLES::DrawActiveTexture(float x, float y, float w, float
|
||||
pos[i * 3 + 1] = pos[i * 3 + 1] * invDestH - 1.0f;
|
||||
}
|
||||
|
||||
// Upscaling postshaders doesn't look well with linear
|
||||
// We always want a plain state here, well, except for when it's used by the stencil stuff...
|
||||
render_->SetDepth(false, false, GL_ALWAYS);
|
||||
render_->SetRaster(false, GL_CCW, GL_FRONT, GL_FALSE);
|
||||
if (!(flags & DRAWTEX_KEEP_STENCIL_ALPHA)) {
|
||||
render_->SetNoBlendAndMask(0xF);
|
||||
render_->SetStencilDisabled();
|
||||
}
|
||||
|
||||
// Upscaling postshaders don't look well with linear
|
||||
if (flags & DRAWTEX_LINEAR) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
render_->SetTextureSampler(0, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_LINEAR, GL_LINEAR, 0.0f);
|
||||
} else {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
render_->SetTextureSampler(0, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST, 0.0f);
|
||||
}
|
||||
|
||||
const GLSLProgram *program = glsl_get_program();
|
||||
if (!program) {
|
||||
ERROR_LOG(FRAMEBUF, "Trying to DrawActiveTexture() without a program");
|
||||
return;
|
||||
}
|
||||
Simple2DVertex verts[4];
|
||||
memcpy(verts[0].pos, &pos[0], 12);
|
||||
memcpy(verts[1].pos, &pos[3], 12);
|
||||
memcpy(verts[3].pos, &pos[6], 12);
|
||||
memcpy(verts[2].pos, &pos[9], 12);
|
||||
memcpy(verts[0].uv, &texCoords[0], 8);
|
||||
memcpy(verts[1].uv, &texCoords[2], 8);
|
||||
memcpy(verts[3].uv, &texCoords[4], 8);
|
||||
memcpy(verts[2].uv, &texCoords[6], 8);
|
||||
|
||||
glEnableVertexAttribArray(program->a_position);
|
||||
glEnableVertexAttribArray(program->a_texcoord0);
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_VAO)) {
|
||||
drawEngineGL_->BindBuffer(pos, sizeof(pos), texCoords, sizeof(texCoords));
|
||||
drawEngineGL_->BindElementBuffer(indices, sizeof(indices));
|
||||
glVertexAttribPointer(program->a_position, 3, GL_FLOAT, GL_FALSE, 12, 0);
|
||||
glVertexAttribPointer(program->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, (void *)sizeof(pos));
|
||||
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0);
|
||||
} else {
|
||||
glstate.arrayBuffer.unbind();
|
||||
glstate.elementArrayBuffer.unbind();
|
||||
glVertexAttribPointer(program->a_position, 3, GL_FLOAT, GL_FALSE, 12, pos);
|
||||
glVertexAttribPointer(program->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, texCoords);
|
||||
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices);
|
||||
}
|
||||
glDisableVertexAttribArray(program->a_position);
|
||||
glDisableVertexAttribArray(program->a_texcoord0);
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::RebindFramebuffer() {
|
||||
FramebufferManagerCommon::RebindFramebuffer();
|
||||
if (g_Config.iRenderingMode == FB_NON_BUFFERED_MODE)
|
||||
glstate.viewport.restore();
|
||||
uint32_t bindOffset;
|
||||
GLRBuffer *buffer;
|
||||
void *dest = drawEngineGL_->GetPushVertexBuffer()->Push(sizeof(verts), &bindOffset, &buffer);
|
||||
memcpy(dest, verts, sizeof(verts));
|
||||
render_->BindVertexBuffer(simple2DInputLayout_, buffer, bindOffset);
|
||||
render_->Draw(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::ReformatFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat old) {
|
||||
@@ -505,6 +468,7 @@ void FramebufferManagerGLES::ReformatFramebufferFrom(VirtualFramebuffer *vfb, GE
|
||||
|
||||
if (old == GE_FORMAT_565) {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE);
|
||||
} else {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
}
|
||||
@@ -527,17 +491,15 @@ void FramebufferManagerGLES::BlitFramebufferDepth(VirtualFramebuffer *src, Virtu
|
||||
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_ARB_FRAMEBUFFER_BLIT | GPU_SUPPORTS_NV_FRAMEBUFFER_BLIT)) {
|
||||
// Let's only do this if not clearing depth.
|
||||
glstate.scissorTest.force(false);
|
||||
draw_->BlitFramebuffer(src->fbo, 0, 0, w, h, dst->fbo, 0, 0, w, h, Draw::FB_DEPTH_BIT, Draw::FB_BLIT_NEAREST);
|
||||
dst->last_frame_depth_updated = gpuStats.numFlips;
|
||||
glstate.scissorTest.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags) {
|
||||
if (!framebuffer->fbo || !useBufferedRendering_) {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
render_->BindTexture(stage, nullptr);
|
||||
gstate_c.skipDrawReason |= SKIPDRAW_BAD_FB_TEXTURE;
|
||||
return;
|
||||
}
|
||||
@@ -565,48 +527,6 @@ void FramebufferManagerGLES::BindFramebufferAsColorTexture(int stage, VirtualFra
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) {
|
||||
PROFILE_THIS_SCOPE("gpu-readback");
|
||||
if (sync) {
|
||||
// flush async just in case when we go for synchronous update
|
||||
// Doesn't actually pack when sent a null argument.
|
||||
PackFramebufferAsync_(nullptr);
|
||||
}
|
||||
|
||||
if (vfb) {
|
||||
// We'll pseudo-blit framebuffers here to get a resized version of vfb.
|
||||
VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb);
|
||||
OptimizeDownloadRange(vfb, x, y, w, h);
|
||||
BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0);
|
||||
|
||||
// PackFramebufferSync_() - Synchronous pixel data transfer using glReadPixels
|
||||
// PackFramebufferAsync_() - Asynchronous pixel data transfer using glReadPixels with PBOs
|
||||
|
||||
if (gl_extensions.IsGLES) {
|
||||
PackFramebufferSync_(nvfb, x, y, w, h);
|
||||
} else {
|
||||
// TODO: Can we fall back to sync without these?
|
||||
if (gl_extensions.ARB_pixel_buffer_object && gstate_c.Supports(GPU_SUPPORTS_OES_TEXTURE_NPOT)) {
|
||||
if (!sync) {
|
||||
PackFramebufferAsync_(nvfb);
|
||||
} else {
|
||||
PackFramebufferSync_(nvfb, x, y, w, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textureCacheGL_->ForgetLastTexture();
|
||||
RebindFramebuffer();
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) {
|
||||
PROFILE_THIS_SCOPE("gpu-readback");
|
||||
// Flush async just in case.
|
||||
PackFramebufferAsync_(nullptr);
|
||||
FramebufferManagerCommon::DownloadFramebufferForClut(fb_address, loadBytes);
|
||||
}
|
||||
|
||||
bool FramebufferManagerGLES::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) {
|
||||
// When updating VRAM, it need to be exact format.
|
||||
if (!gstate_c.Supports(GPU_PREFER_CPU_DOWNLOAD)) {
|
||||
@@ -641,12 +561,10 @@ void FramebufferManagerGLES::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb)
|
||||
// Discard the previous contents of this buffer where possible.
|
||||
if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) {
|
||||
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
|
||||
GLenum attachments[3] = { GL_COLOR_ATTACHMENT0, GL_STENCIL_ATTACHMENT, GL_DEPTH_ATTACHMENT };
|
||||
glInvalidateFramebuffer(GL_FRAMEBUFFER, 3, attachments);
|
||||
} else if (gl_extensions.IsGLES) {
|
||||
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE);
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) {
|
||||
@@ -699,12 +617,10 @@ void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX,
|
||||
const bool yOverlap = src == dst && srcY2 > dstY1 && srcY1 < dstY2;
|
||||
if (sameSize && sameDepth && srcInsideBounds && dstInsideBounds && !(xOverlap && yOverlap)) {
|
||||
draw_->CopyFramebufferImage(src->fbo, 0, srcX1, srcY1, 0, dst->fbo, 0, dstX1, dstY1, 0, dstX2 - dstX1, dstY2 - dstY1, 1, Draw::FB_COLOR_BIT);
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
glstate.scissorTest.force(false);
|
||||
if (useBlit) {
|
||||
draw_->BlitFramebuffer(src->fbo, srcX1, srcY1, srcX2, srcY2, dst->fbo, dstX1, dstY1, dstX2, dstY2, Draw::FB_COLOR_BIT, Draw::FB_BLIT_NEAREST);
|
||||
} else {
|
||||
@@ -714,43 +630,23 @@ void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX,
|
||||
// Make sure our 2D drawing program is ready. Compiles only if not already compiled.
|
||||
CompileDraw2DProgram();
|
||||
|
||||
glstate.viewport.force(0, 0, dst->renderWidth, dst->renderHeight);
|
||||
glstate.blend.force(false);
|
||||
glstate.cullFace.force(false);
|
||||
glstate.depthTest.force(false);
|
||||
glstate.stencilTest.force(false);
|
||||
#if !defined(USING_GLES2)
|
||||
glstate.colorLogicOp.force(false);
|
||||
#endif
|
||||
glstate.colorMask.force(true, true, true, true);
|
||||
glstate.stencilMask.force(0xFF);
|
||||
render_->SetViewport({ 0, 0, (float)dst->renderWidth, (float)dst->renderHeight, 0, 1.0f });
|
||||
render_->SetStencilDisabled();
|
||||
render_->SetDepth(false, false, GL_ALWAYS);
|
||||
render_->SetNoBlendAndMask(0xF);
|
||||
|
||||
// The first four coordinates are relative to the 6th and 7th arguments of DrawActiveTexture.
|
||||
// Should maybe revamp that interface.
|
||||
float srcW = src->bufferWidth;
|
||||
float srcH = src->bufferHeight;
|
||||
glsl_bind(draw2dprogram_);
|
||||
render_->BindProgram(draw2dprogram_);
|
||||
DrawActiveTexture(dstX1, dstY1, w * dstXFactor, h, dst->bufferWidth, dst->bufferHeight, srcX1 / srcW, srcY1 / srcH, srcX2 / srcW, srcY2 / srcH, ROTATION_LOCKED_HORIZONTAL, DRAWTEX_NEAREST);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
textureCacheGL_->ForgetLastTexture();
|
||||
glstate.viewport.restore();
|
||||
glstate.blend.restore();
|
||||
glstate.cullFace.restore();
|
||||
glstate.depthTest.restore();
|
||||
glstate.stencilTest.restore();
|
||||
#if !defined(USING_GLES2)
|
||||
glstate.colorLogicOp.restore();
|
||||
#endif
|
||||
glstate.colorMask.restore();
|
||||
glstate.stencilMask.restore();
|
||||
}
|
||||
|
||||
glstate.scissorTest.restore();
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE);
|
||||
}
|
||||
|
||||
// TODO: SSE/NEON
|
||||
// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :)
|
||||
void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format) {
|
||||
// Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP.
|
||||
const u32 *src32 = (const u32 *)src;
|
||||
@@ -806,193 +702,6 @@ void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) {
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
GLubyte *packed = 0;
|
||||
bool unbind = false;
|
||||
const u8 nextPBO = (currentPBO_ + 1) % MAX_PBO;
|
||||
const bool useCPU = gstate_c.Supports(GPU_PREFER_CPU_DOWNLOAD);
|
||||
|
||||
// We'll prepare two PBOs to switch between readying and reading
|
||||
if (!pixelBufObj_) {
|
||||
if (!vfb) {
|
||||
// This call is just to flush the buffers. We don't have any yet,
|
||||
// so there's nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
GLuint pbos[MAX_PBO];
|
||||
glGenBuffers(MAX_PBO, pbos);
|
||||
|
||||
pixelBufObj_ = new AsyncPBO[MAX_PBO];
|
||||
for (int i = 0; i < MAX_PBO; i++) {
|
||||
pixelBufObj_[i].handle = pbos[i];
|
||||
pixelBufObj_[i].maxSize = 0;
|
||||
pixelBufObj_[i].reading = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Receive previously requested data from a PBO
|
||||
AsyncPBO &pbo = pixelBufObj_[nextPBO];
|
||||
if (pbo.reading) {
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo.handle);
|
||||
#ifdef USING_GLES2
|
||||
// Not on desktop GL 2.x...
|
||||
packed = (GLubyte *)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, pbo.size, GL_MAP_READ_BIT);
|
||||
#else
|
||||
packed = (GLubyte *)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
|
||||
#endif
|
||||
|
||||
if (packed) {
|
||||
DEBUG_LOG(FRAMEBUF, "Reading PBO to memory , bufSize = %u, packed = %p, fb_address = %08x, stride = %u, pbo = %u",
|
||||
pbo.size, packed, pbo.fb_address, pbo.stride, nextPBO);
|
||||
|
||||
if (useCPU) {
|
||||
u8 *dst = Memory::GetPointer(pbo.fb_address);
|
||||
ConvertFromRGBA8888(dst, packed, pbo.stride, pbo.stride, pbo.stride, pbo.height, pbo.format);
|
||||
} else {
|
||||
// We don't need to convert, GPU already did (or should have)
|
||||
Memory::MemcpyUnchecked(pbo.fb_address, packed, pbo.size);
|
||||
}
|
||||
|
||||
pbo.reading = false;
|
||||
}
|
||||
|
||||
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||
unbind = true;
|
||||
}
|
||||
|
||||
// Order packing/readback of the framebuffer
|
||||
if (vfb) {
|
||||
bool reverseOrder = gstate_c.Supports(GPU_PREFER_REVERSE_COLOR_ORDER);
|
||||
Draw::DataFormat dataFmt = Draw::DataFormat::UNDEFINED;
|
||||
switch (vfb->format) {
|
||||
case GE_FORMAT_4444:
|
||||
dataFmt = (reverseOrder ? Draw::DataFormat::A4R4G4B4_UNORM_PACK16 : Draw::DataFormat::B4G4R4A4_UNORM_PACK16);
|
||||
break;
|
||||
case GE_FORMAT_5551:
|
||||
dataFmt = (reverseOrder ? Draw::DataFormat::A1R5G5B5_UNORM_PACK16 : Draw::DataFormat::B5G5R5A1_UNORM_PACK16);
|
||||
break;
|
||||
case GE_FORMAT_565:
|
||||
dataFmt = (reverseOrder ? Draw::DataFormat::R5G6B5_UNORM_PACK16 : Draw::DataFormat::B5G6R5_UNORM_PACK16);
|
||||
break;
|
||||
case GE_FORMAT_8888:
|
||||
dataFmt = Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
break;
|
||||
};
|
||||
|
||||
if (useCPU) {
|
||||
dataFmt = Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
}
|
||||
|
||||
|
||||
int pixelSize = (int)DataFormatSizeInBytes(dataFmt);
|
||||
int align = pixelSize;
|
||||
|
||||
// If using the CPU, we need 4 bytes per pixel always.
|
||||
u32 bufSize = vfb->fb_stride * vfb->height * pixelSize;
|
||||
u32 fb_address = (0x04000000) | vfb->fb_address;
|
||||
|
||||
if (!vfb->fbo) {
|
||||
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferAsync_: vfb->fbo == 0");
|
||||
return;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, pixelBufObj_[currentPBO_].handle);
|
||||
|
||||
if (pixelBufObj_[currentPBO_].maxSize < bufSize) {
|
||||
// We reserve a buffer big enough to fit all those pixels
|
||||
glBufferData(GL_PIXEL_PACK_BUFFER, bufSize, NULL, GL_DYNAMIC_READ);
|
||||
pixelBufObj_[currentPBO_].maxSize = bufSize;
|
||||
}
|
||||
|
||||
// TODO: This is a hack since PBOs have not been implemented in Thin3D yet(and maybe shouldn't? maybe should do this internally?)
|
||||
draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, 0, vfb->fb_stride, vfb->height, dataFmt, nullptr, vfb->fb_stride);
|
||||
|
||||
unbind = true;
|
||||
|
||||
pixelBufObj_[currentPBO_].fb_address = fb_address;
|
||||
pixelBufObj_[currentPBO_].size = bufSize;
|
||||
pixelBufObj_[currentPBO_].stride = vfb->fb_stride;
|
||||
pixelBufObj_[currentPBO_].height = vfb->height;
|
||||
pixelBufObj_[currentPBO_].format = vfb->format;
|
||||
pixelBufObj_[currentPBO_].reading = true;
|
||||
}
|
||||
|
||||
currentPBO_ = nextPBO;
|
||||
|
||||
if (unbind) {
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
|
||||
if (!vfb->fbo) {
|
||||
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferSync_: vfb->fbo == 0");
|
||||
return;
|
||||
}
|
||||
|
||||
int possibleH = std::max(vfb->height - y, 0);
|
||||
if (h > possibleH) {
|
||||
h = possibleH;
|
||||
}
|
||||
|
||||
bool convert = vfb->format != GE_FORMAT_8888;
|
||||
const int dstBpp = vfb->format == GE_FORMAT_8888 ? 4 : 2;
|
||||
const int packWidth = std::min(vfb->fb_stride, std::min(x + w, (int)vfb->width));
|
||||
|
||||
// Pixel size always 4 here because we always request RGBA8888
|
||||
u32 bufSize = packWidth * h * 4;
|
||||
u32 fb_address = 0x04000000 | vfb->fb_address;
|
||||
|
||||
if (gl_extensions.IsGLES && !gl_extensions.GLES3 && packWidth != vfb->fb_stride && h != 1) {
|
||||
// Need to use a temp buffer, since GLES2 doesn't support GL_PACK_ROW_LENGTH.
|
||||
convert = true;
|
||||
}
|
||||
|
||||
int dstByteOffset = y * vfb->fb_stride * dstBpp;
|
||||
u8 *dst = Memory::GetPointer(fb_address + dstByteOffset);
|
||||
|
||||
u8 *packed = nullptr;
|
||||
if (!convert) {
|
||||
packed = (u8 *)dst;
|
||||
} else {
|
||||
// End result may be 16-bit but we are reading 32-bit, so there may not be enough space at fb_address
|
||||
if (!convBuf_ || convBufSize_ < bufSize) {
|
||||
delete [] convBuf_;
|
||||
convBuf_ = new u8[bufSize];
|
||||
convBufSize_ = bufSize;
|
||||
}
|
||||
packed = convBuf_;
|
||||
}
|
||||
|
||||
if (packed) {
|
||||
DEBUG_LOG(FRAMEBUF, "Reading framebuffer to mem, bufSize = %u, fb_address = %08x", bufSize, fb_address);
|
||||
// Avoid reading the part between width and stride, if possible.
|
||||
int packStride = convert || h == 1 ? packWidth : vfb->fb_stride;
|
||||
draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, y, packWidth, h, Draw::DataFormat::R8G8B8A8_UNORM, packed, packStride);
|
||||
|
||||
if (convert) {
|
||||
ConvertFromRGBA8888(dst, packed, vfb->fb_stride, packStride, packWidth, h, vfb->format);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Move this into Thin3d.
|
||||
if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) {
|
||||
#ifdef USING_GLES2
|
||||
// GLES3 doesn't support using GL_READ_FRAMEBUFFER here.
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
|
||||
const GLenum target = GL_FRAMEBUFFER;
|
||||
#else
|
||||
const GLenum target = GL_READ_FRAMEBUFFER;
|
||||
#endif
|
||||
GLenum attachments[3] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT };
|
||||
glInvalidateFramebuffer(target, 3, attachments);
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
|
||||
if (!vfb->fbo) {
|
||||
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackDepthbuffer: vfb->fbo == 0");
|
||||
@@ -1033,26 +742,9 @@ void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int
|
||||
}
|
||||
}
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::EndFrame() {
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
|
||||
// Let's explicitly invalidate any temp FBOs used during this frame.
|
||||
if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) {
|
||||
for (auto temp : tempFBOs_) {
|
||||
if (temp.second.last_frame_used < gpuStats.numFlips) {
|
||||
continue;
|
||||
}
|
||||
|
||||
draw_->BindFramebufferAsRenderTarget(temp.second.fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
|
||||
GLenum attachments[3] = { GL_COLOR_ATTACHMENT0, GL_STENCIL_ATTACHMENT, GL_DEPTH_ATTACHMENT };
|
||||
glInvalidateFramebuffer(GL_FRAMEBUFFER, 3, attachments);
|
||||
}
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP , Draw::RPAction::KEEP, Draw::RPAction::KEEP });
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::DeviceLost() {
|
||||
@@ -1062,11 +754,11 @@ void FramebufferManagerGLES::DeviceLost() {
|
||||
|
||||
void FramebufferManagerGLES::DeviceRestore(Draw::DrawContext *draw) {
|
||||
draw_ = draw;
|
||||
render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
CreateDeviceObjects();
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::DestroyAllFBOs() {
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
currentRenderVfb_ = 0;
|
||||
displayFramebuf_ = 0;
|
||||
prevDisplayFramebuf_ = 0;
|
||||
@@ -1091,27 +783,20 @@ void FramebufferManagerGLES::DestroyAllFBOs() {
|
||||
tempFBOs_.clear();
|
||||
|
||||
SetNumExtraFBOs(0);
|
||||
|
||||
DisableState();
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::Resized() {
|
||||
FramebufferManagerCommon::Resized();
|
||||
|
||||
render_->Resize(PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight);
|
||||
if (UpdateSize()) {
|
||||
DestroyAllFBOs();
|
||||
}
|
||||
|
||||
#ifndef USING_GLES2
|
||||
if (g_Config.iInternalResolution == 0) {
|
||||
glLineWidth(std::max(1, (int)(renderWidth_ / 480)));
|
||||
glPointSize(std::max(1.0f, (float)(renderWidth_ / 480.f)));
|
||||
} else {
|
||||
glLineWidth(g_Config.iInternalResolution);
|
||||
glPointSize((float)g_Config.iInternalResolution);
|
||||
}
|
||||
#endif
|
||||
// Might have a new post shader - let's compile it.
|
||||
CompilePostShader();
|
||||
|
||||
// render_->SetLineWidth(renderWidth_ / 480.0f);
|
||||
}
|
||||
|
||||
bool FramebufferManagerGLES::GetOutputFramebuffer(GPUDebugBuffer &buffer) {
|
||||
|
||||
@@ -29,28 +29,16 @@
|
||||
#include "Core/Config.h"
|
||||
#include "GPU/GPUCommon.h"
|
||||
#include "GPU/Common/FramebufferCommon.h"
|
||||
#include "thin3d/GLRenderManager.h"
|
||||
|
||||
struct GLSLProgram;
|
||||
class TextureCacheGLES;
|
||||
class DrawEngineGLES;
|
||||
class ShaderManagerGLES;
|
||||
|
||||
// Simple struct for asynchronous PBO readbacks
|
||||
struct AsyncPBO {
|
||||
uint32_t handle;
|
||||
u32 maxSize;
|
||||
|
||||
u32 fb_address;
|
||||
u32 stride;
|
||||
u32 height;
|
||||
u32 size;
|
||||
GEBufferFormat format;
|
||||
bool reading;
|
||||
};
|
||||
|
||||
class FramebufferManagerGLES : public FramebufferManagerCommon {
|
||||
public:
|
||||
FramebufferManagerGLES(Draw::DrawContext *draw);
|
||||
FramebufferManagerGLES(Draw::DrawContext *draw, GLRenderManager *render);
|
||||
~FramebufferManagerGLES();
|
||||
|
||||
void SetTextureCache(TextureCacheGLES *tc);
|
||||
@@ -66,7 +54,6 @@ public:
|
||||
void EndFrame();
|
||||
void Resized() override;
|
||||
void DeviceLost();
|
||||
void SetLineWidth();
|
||||
void ReformatFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat old) override;
|
||||
|
||||
void BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst) override;
|
||||
@@ -74,20 +61,14 @@ public:
|
||||
// For use when texturing from a framebuffer. May create a duplicate if target.
|
||||
void BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags);
|
||||
|
||||
// Reads a rectangular subregion of a framebuffer to the right position in its backing memory.
|
||||
void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) override;
|
||||
void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) override;
|
||||
|
||||
bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override;
|
||||
|
||||
bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override;
|
||||
virtual void RebindFramebuffer() override;
|
||||
|
||||
void DeviceRestore(Draw::DrawContext *draw);
|
||||
|
||||
protected:
|
||||
void SetViewport2D(int x, int y, int w, int h) override;
|
||||
void DisableState() override;
|
||||
|
||||
// Used by ReadFramebufferToMemory and later framebuffer block copies
|
||||
void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) override;
|
||||
@@ -103,35 +84,44 @@ private:
|
||||
void Bind2DShader() override;
|
||||
void BindPostShader(const PostShaderUniforms &uniforms) override;
|
||||
void CompileDraw2DProgram();
|
||||
void DestroyDraw2DProgram();
|
||||
void CompilePostShader();
|
||||
|
||||
void PackFramebufferAsync_(VirtualFramebuffer *vfb); // Not used under ES currently
|
||||
void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) override;
|
||||
void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h);
|
||||
|
||||
GLRenderManager *render_;
|
||||
|
||||
// Used by DrawPixels
|
||||
unsigned int drawPixelsTex_;
|
||||
GEBufferFormat drawPixelsTexFormat_;
|
||||
int drawPixelsTexW_;
|
||||
int drawPixelsTexH_;
|
||||
GLRTexture *drawPixelsTex_ = nullptr;
|
||||
GEBufferFormat drawPixelsTexFormat_ = GE_FORMAT_INVALID;
|
||||
int drawPixelsTexW_ = 0;
|
||||
int drawPixelsTexH_ = 0;
|
||||
|
||||
u8 *convBuf_;
|
||||
u32 convBufSize_;
|
||||
GLSLProgram *draw2dprogram_ = nullptr;
|
||||
GLSLProgram *postShaderProgram_ = nullptr;
|
||||
GLSLProgram *stencilUploadProgram_ = nullptr;
|
||||
int plainColorLoc_;
|
||||
int videoLoc_;
|
||||
int timeLoc_;
|
||||
int pixelDeltaLoc_;
|
||||
int deltaLoc_;
|
||||
u8 *convBuf_ = nullptr;
|
||||
u32 convBufSize_ = 0;
|
||||
GLRProgram *draw2dprogram_ = nullptr;
|
||||
GLRProgram *postShaderProgram_ = nullptr;
|
||||
|
||||
TextureCacheGLES *textureCacheGL_;
|
||||
ShaderManagerGLES *shaderManagerGL_;
|
||||
DrawEngineGLES *drawEngineGL_;
|
||||
GLRProgram *stencilUploadProgram_ = nullptr;
|
||||
int u_stencilUploadTex = -1;
|
||||
int u_stencilValue = -1;
|
||||
int u_postShaderTex = -1;
|
||||
|
||||
// Cached uniform locs
|
||||
int u_draw2d_tex = -1;
|
||||
|
||||
// Not used under ES currently.
|
||||
AsyncPBO *pixelBufObj_; //this isn't that large
|
||||
u8 currentPBO_;
|
||||
int plainColorLoc_ = -1;
|
||||
int videoLoc_ = -1;
|
||||
int timeLoc_ = -1;
|
||||
int pixelDeltaLoc_ = -1;
|
||||
int deltaLoc_ = -1;
|
||||
|
||||
TextureCacheGLES *textureCacheGL_ = nullptr;
|
||||
ShaderManagerGLES *shaderManagerGL_ = nullptr;
|
||||
DrawEngineGLES *drawEngineGL_ = nullptr;
|
||||
|
||||
struct Simple2DVertex {
|
||||
float pos[3];
|
||||
float uv[2];
|
||||
};
|
||||
GLRInputLayout *simple2DInputLayout_ = nullptr;
|
||||
};
|
||||
+34
-259
@@ -36,7 +36,6 @@
|
||||
#include "GPU/GeDisasm.h"
|
||||
#include "GPU/Common/FramebufferCommon.h"
|
||||
|
||||
#include "ext/native/gfx/GLStateCache.h"
|
||||
#include "GPU/GLES/ShaderManagerGLES.h"
|
||||
#include "GPU/GLES/GPU_GLES.h"
|
||||
#include "GPU/GLES/FramebufferManagerGLES.h"
|
||||
@@ -52,39 +51,15 @@
|
||||
#include "Windows/GPU/WindowsGLContext.h"
|
||||
#endif
|
||||
|
||||
struct GLESCommandTableEntry {
|
||||
uint8_t cmd;
|
||||
uint8_t flags;
|
||||
uint64_t dirty;
|
||||
GPU_GLES::CmdFunc func;
|
||||
};
|
||||
|
||||
// This table gets crunched into a faster form by init.
|
||||
// TODO: Share this table between the backends. Will have to make another indirection for the function pointers though..
|
||||
static const GLESCommandTableEntry commandTable[] = {
|
||||
// Changes that dirty the current texture.
|
||||
{ GE_CMD_TEXSIZE0, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPUCommon::Execute_TexSize0 },
|
||||
|
||||
// Changing the vertex type requires us to flush.
|
||||
{ GE_CMD_VERTEXTYPE, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_VertexType },
|
||||
|
||||
{ GE_CMD_PRIM, FLAG_EXECUTE, 0, &GPU_GLES::Execute_Prim },
|
||||
{ GE_CMD_BEZIER, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPUCommon::Execute_Bezier },
|
||||
{ GE_CMD_SPLINE, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPUCommon::Execute_Spline },
|
||||
|
||||
// Changes that trigger data copies. Only flushing on change for LOADCLUT must be a bit of a hack...
|
||||
{ GE_CMD_LOADCLUT, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPU_GLES::Execute_LoadClut },
|
||||
};
|
||||
|
||||
GPU_GLES::CommandInfo GPU_GLES::cmdInfo_[256];
|
||||
|
||||
GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
||||
: GPUCommon(gfxCtx, draw) {
|
||||
: GPUCommon(gfxCtx, draw), drawEngine_(draw), fragmentTestCache_(draw), depalShaderCache_(draw) {
|
||||
UpdateVsyncInterval(true);
|
||||
CheckGPUFeatures();
|
||||
|
||||
shaderManagerGL_ = new ShaderManagerGLES();
|
||||
framebufferManagerGL_ = new FramebufferManagerGLES(draw);
|
||||
GLRenderManager *render = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
|
||||
shaderManagerGL_ = new ShaderManagerGLES(render);
|
||||
framebufferManagerGL_ = new FramebufferManagerGLES(draw, render);
|
||||
framebufferManager_ = framebufferManagerGL_;
|
||||
textureCacheGL_ = new TextureCacheGLES(draw);
|
||||
textureCache_ = textureCacheGL_;
|
||||
@@ -111,57 +86,13 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
||||
ERROR_LOG(G3D, "gstate has drifted out of sync!");
|
||||
}
|
||||
|
||||
memset(cmdInfo_, 0, sizeof(cmdInfo_));
|
||||
|
||||
// Import both the global and local command tables, and check for dupes
|
||||
std::set<u8> dupeCheck;
|
||||
for (size_t i = 0; i < commonCommandTableSize; i++) {
|
||||
const u8 cmd = commonCommandTable[i].cmd;
|
||||
if (dupeCheck.find(cmd) != dupeCheck.end()) {
|
||||
ERROR_LOG(G3D, "Command table Dupe: %02x (%i)", (int)cmd, (int)cmd);
|
||||
} else {
|
||||
dupeCheck.insert(cmd);
|
||||
}
|
||||
cmdInfo_[cmd].flags |= (uint64_t)commonCommandTable[i].flags | (commonCommandTable[i].dirty << 8);
|
||||
cmdInfo_[cmd].func = commonCommandTable[i].func;
|
||||
if ((cmdInfo_[cmd].flags & (FLAG_EXECUTE | FLAG_EXECUTEONCHANGE)) && !cmdInfo_[cmd].func) {
|
||||
Crash();
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(commandTable); i++) {
|
||||
const u8 cmd = commandTable[i].cmd;
|
||||
if (dupeCheck.find(cmd) != dupeCheck.end()) {
|
||||
ERROR_LOG(G3D, "Command table Dupe: %02x (%i)", (int)cmd, (int)cmd);
|
||||
} else {
|
||||
dupeCheck.insert(cmd);
|
||||
}
|
||||
cmdInfo_[cmd].flags |= (uint64_t)commandTable[i].flags | (commandTable[i].dirty << 8);
|
||||
cmdInfo_[cmd].func = commandTable[i].func;
|
||||
if ((cmdInfo_[cmd].flags & (FLAG_EXECUTE | FLAG_EXECUTEONCHANGE)) && !cmdInfo_[cmd].func) {
|
||||
Crash();
|
||||
}
|
||||
}
|
||||
// Find commands missing from the table.
|
||||
for (int i = 0; i < 0xEF; i++) {
|
||||
if (dupeCheck.find((u8)i) == dupeCheck.end()) {
|
||||
ERROR_LOG(G3D, "Command missing from table: %02x (%i)", i, i);
|
||||
}
|
||||
}
|
||||
|
||||
// No need to flush before the tex scale/offset commands if we are baking
|
||||
// the tex scale/offset into the vertices anyway.
|
||||
|
||||
UpdateCmdInfo();
|
||||
|
||||
BuildReportingInfo();
|
||||
// Update again after init to be sure of any silly driver problems.
|
||||
UpdateVsyncInterval(true);
|
||||
|
||||
// Some of our defaults are different from hw defaults, let's assert them.
|
||||
// We restore each frame anyway, but here is convenient for tests.
|
||||
glstate.Restore();
|
||||
drawEngine_.RestoreVAO();
|
||||
textureCacheGL_->NotifyConfigChanged();
|
||||
|
||||
// Load shader cache.
|
||||
@@ -187,6 +118,12 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
||||
}
|
||||
|
||||
GPU_GLES::~GPU_GLES() {
|
||||
GLRenderManager *render = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
render->Wipe();
|
||||
|
||||
// If we're here during app shutdown (exiting the Windows app in-game, for example)
|
||||
// everything should already be cleared since DeviceLost has been run.
|
||||
|
||||
framebufferManagerGL_->DestroyAllFBOs();
|
||||
shaderManagerGL_->ClearCache(true);
|
||||
depalShaderCache_.Clear();
|
||||
@@ -298,9 +235,6 @@ void GPU_GLES::CheckGPUFeatures() {
|
||||
if (gl_extensions.OES_texture_npot)
|
||||
features |= GPU_SUPPORTS_OES_TEXTURE_NPOT;
|
||||
|
||||
if (gl_extensions.EXT_unpack_subimage)
|
||||
features |= GPU_SUPPORTS_UNPACK_SUBIMAGE;
|
||||
|
||||
if (gl_extensions.EXT_blend_minmax)
|
||||
features |= GPU_SUPPORTS_BLEND_MINMAX;
|
||||
|
||||
@@ -322,8 +256,7 @@ void GPU_GLES::CheckGPUFeatures() {
|
||||
if (instanceRendering)
|
||||
features |= GPU_SUPPORTS_INSTANCE_RENDERING;
|
||||
|
||||
int maxVertexTextureImageUnits;
|
||||
glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextureImageUnits);
|
||||
int maxVertexTextureImageUnits = gl_extensions.maxVertexTextureUnits;
|
||||
if (maxVertexTextureImageUnits >= 3) // At least 3 for hardware tessellation
|
||||
features |= GPU_SUPPORTS_VERTEX_TEXTURE_FETCH;
|
||||
|
||||
@@ -368,30 +301,23 @@ bool GPU_GLES::IsReady() {
|
||||
return shaderManagerGL_->ContinuePrecompile();
|
||||
}
|
||||
|
||||
// Let's avoid passing nulls into snprintf().
|
||||
static const char *GetGLStringAlways(GLenum name) {
|
||||
const GLubyte *value = glGetString(name);
|
||||
if (!value)
|
||||
return "?";
|
||||
return (const char *)value;
|
||||
}
|
||||
|
||||
// Needs to be called on GPU thread, not reporting thread.
|
||||
void GPU_GLES::BuildReportingInfo() {
|
||||
const char *glVendor = GetGLStringAlways(GL_VENDOR);
|
||||
const char *glRenderer = GetGLStringAlways(GL_RENDERER);
|
||||
const char *glVersion = GetGLStringAlways(GL_VERSION);
|
||||
const char *glSlVersion = GetGLStringAlways(GL_SHADING_LANGUAGE_VERSION);
|
||||
const char *glExtensions = nullptr;
|
||||
GLRenderManager *render = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
|
||||
std::string glVendor = render->GetGLString(GL_VENDOR);
|
||||
std::string glRenderer = render->GetGLString(GL_RENDERER);
|
||||
std::string glVersion = render->GetGLString(GL_VERSION);
|
||||
std::string glSlVersion = render->GetGLString(GL_SHADING_LANGUAGE_VERSION);
|
||||
std::string glExtensions;
|
||||
|
||||
if (gl_extensions.VersionGEThan(3, 0)) {
|
||||
glExtensions = g_all_gl_extensions.c_str();
|
||||
glExtensions = g_all_gl_extensions;
|
||||
} else {
|
||||
glExtensions = GetGLStringAlways(GL_EXTENSIONS);
|
||||
glExtensions = render->GetGLString(GL_EXTENSIONS);
|
||||
}
|
||||
|
||||
char temp[16384];
|
||||
snprintf(temp, sizeof(temp), "%s (%s %s), %s (extensions: %s)", glVersion, glVendor, glRenderer, glSlVersion, glExtensions);
|
||||
snprintf(temp, sizeof(temp), "%s (%s %s), %s (extensions: %s)", glVersion.c_str(), glVendor.c_str(), glRenderer.c_str(), glSlVersion.c_str(), glExtensions.c_str());
|
||||
reportingPrimaryInfo_ = glVendor;
|
||||
reportingFullInfo_ = temp;
|
||||
|
||||
@@ -401,6 +327,10 @@ void GPU_GLES::BuildReportingInfo() {
|
||||
void GPU_GLES::DeviceLost() {
|
||||
ILOG("GPU_GLES: DeviceLost");
|
||||
|
||||
// Simply drop all caches and textures.
|
||||
// FBOs appear to survive? Or no?
|
||||
// TransformDraw has registered as a GfxResourceHolder.
|
||||
drawEngine_.ClearInputLayoutMap();
|
||||
shaderManagerGL_->ClearCache(false);
|
||||
textureCacheGL_->Clear(false);
|
||||
fragmentTestCache_.Clear(false);
|
||||
@@ -413,7 +343,6 @@ void GPU_GLES::DeviceRestore() {
|
||||
draw_ = (Draw::DrawContext *)PSP_CoreParameter().graphicsContext->GetDrawContext();
|
||||
ILOG("GPU_GLES: DeviceRestore");
|
||||
|
||||
UpdateCmdInfo();
|
||||
UpdateVsyncInterval(true);
|
||||
|
||||
textureCacheGL_->DeviceRestore(draw_);
|
||||
@@ -429,19 +358,10 @@ void GPU_GLES::Reinitialize() {
|
||||
}
|
||||
|
||||
void GPU_GLES::InitClear() {
|
||||
bool useNonBufferedRendering = g_Config.iRenderingMode == FB_NON_BUFFERED_MODE;
|
||||
if (useNonBufferedRendering) {
|
||||
glstate.depthWrite.set(GL_TRUE);
|
||||
glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glClearColor(0,0,0,1);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
}
|
||||
glstate.viewport.set(0, 0, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight);
|
||||
}
|
||||
|
||||
void GPU_GLES::BeginHostFrame() {
|
||||
GPUCommon::BeginHostFrame();
|
||||
UpdateCmdInfo();
|
||||
if (resized_) {
|
||||
CheckGPUFeatures();
|
||||
framebufferManager_->Resized();
|
||||
@@ -449,6 +369,12 @@ void GPU_GLES::BeginHostFrame() {
|
||||
shaderManagerGL_->DirtyShader();
|
||||
textureCacheGL_->NotifyConfigChanged();
|
||||
}
|
||||
|
||||
drawEngine_.BeginFrame();
|
||||
}
|
||||
|
||||
void GPU_GLES::EndHostFrame() {
|
||||
drawEngine_.EndFrame();
|
||||
}
|
||||
|
||||
inline void GPU_GLES::UpdateVsyncInterval(bool force) {
|
||||
@@ -478,19 +404,7 @@ inline void GPU_GLES::UpdateVsyncInterval(bool force) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void GPU_GLES::UpdateCmdInfo() {
|
||||
if (g_Config.bSoftwareSkinning) {
|
||||
cmdInfo_[GE_CMD_VERTEXTYPE].flags &= ~FLAG_FLUSHBEFOREONCHANGE;
|
||||
cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommon::Execute_VertexTypeSkinning;
|
||||
} else {
|
||||
cmdInfo_[GE_CMD_VERTEXTYPE].flags |= FLAG_FLUSHBEFOREONCHANGE;
|
||||
cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommon::Execute_VertexType;
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_GLES::ReapplyGfxState() {
|
||||
drawEngine_.RestoreVAO();
|
||||
glstate.Restore();
|
||||
GPUCommon::ReapplyGfxState();
|
||||
}
|
||||
|
||||
@@ -500,14 +414,13 @@ void GPU_GLES::BeginFrame() {
|
||||
|
||||
textureCacheGL_->StartFrame();
|
||||
drawEngine_.DecimateTrackedVertexArrays();
|
||||
drawEngine_.DecimateBuffers();
|
||||
depalShaderCache_.Decimate();
|
||||
fragmentTestCache_.Decimate();
|
||||
|
||||
GPUCommon::BeginFrame();
|
||||
|
||||
// Save the cache from time to time. TODO: How often?
|
||||
if (!shaderCachePath_.empty() && (gpuStats.numFlips & 1023) == 0) {
|
||||
// Save the cache from time to time. TODO: How often? We save on exit, so shouldn't need to do this all that often.
|
||||
if (!shaderCachePath_.empty() && (gpuStats.numFlips & 4095) == 0) {
|
||||
shaderManagerGL_->Save(shaderCachePath_);
|
||||
}
|
||||
|
||||
@@ -524,26 +437,6 @@ void GPU_GLES::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat fo
|
||||
framebufferManagerGL_->SetDisplayFramebuffer(framebuf, stride, format);
|
||||
}
|
||||
|
||||
bool GPU_GLES::FramebufferDirty() {
|
||||
VirtualFramebuffer *vfb = framebufferManagerGL_->GetDisplayVFB();
|
||||
if (vfb) {
|
||||
bool dirty = vfb->dirtyAfterDisplay;
|
||||
vfb->dirtyAfterDisplay = false;
|
||||
return dirty;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU_GLES::FramebufferReallyDirty() {
|
||||
VirtualFramebuffer *vfb = framebufferManagerGL_->GetDisplayVFB();
|
||||
if (vfb) {
|
||||
bool dirty = vfb->reallyDirtyAfterDisplay;
|
||||
vfb->reallyDirtyAfterDisplay = false;
|
||||
return dirty;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPU_GLES::CopyDisplayToOutput() {
|
||||
// Flush anything left over.
|
||||
framebufferManagerGL_->RebindFramebuffer();
|
||||
@@ -551,9 +444,6 @@ void GPU_GLES::CopyDisplayToOutput() {
|
||||
|
||||
shaderManagerGL_->DirtyLastShader();
|
||||
|
||||
glstate.depthWrite.set(GL_TRUE);
|
||||
glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
|
||||
framebufferManagerGL_->CopyDisplayToOutput();
|
||||
framebufferManagerGL_->EndFrame();
|
||||
|
||||
@@ -568,44 +458,6 @@ void GPU_GLES::CopyDisplayToOutput() {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Maybe should write this in ASM...
|
||||
void GPU_GLES::FastRunLoop(DisplayList &list) {
|
||||
PROFILE_THIS_SCOPE("gpuloop");
|
||||
const CommandInfo *cmdInfo = cmdInfo_;
|
||||
int dc = downcount;
|
||||
for (; dc > 0; --dc) {
|
||||
// We know that display list PCs have the upper nibble == 0 - no need to mask the pointer
|
||||
const u32 op = *(const u32 *)(Memory::base + list.pc);
|
||||
const u32 cmd = op >> 24;
|
||||
const CommandInfo &info = cmdInfo[cmd];
|
||||
const u32 diff = op ^ gstate.cmdmem[cmd];
|
||||
if (diff == 0) {
|
||||
if (info.flags & FLAG_EXECUTE) {
|
||||
downcount = dc;
|
||||
(this->*info.func)(op, diff);
|
||||
dc = downcount;
|
||||
}
|
||||
} else {
|
||||
uint64_t flags = info.flags;
|
||||
if (flags & FLAG_FLUSHBEFOREONCHANGE) {
|
||||
drawEngine_.Flush();
|
||||
}
|
||||
gstate.cmdmem[cmd] = op;
|
||||
if (flags & (FLAG_EXECUTE | FLAG_EXECUTEONCHANGE)) {
|
||||
downcount = dc;
|
||||
(this->*info.func)(op, diff);
|
||||
dc = downcount;
|
||||
} else {
|
||||
uint64_t dirty = flags >> 8;
|
||||
if (dirty)
|
||||
gstate_c.Dirty(dirty);
|
||||
}
|
||||
}
|
||||
list.pc += 4;
|
||||
}
|
||||
downcount = 0;
|
||||
}
|
||||
|
||||
void GPU_GLES::FinishDeferred() {
|
||||
// This finishes reading any vertex data that is pending.
|
||||
drawEngine_.FinishDeferred();
|
||||
@@ -638,81 +490,6 @@ void GPU_GLES::ExecuteOp(u32 op, u32 diff) {
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_GLES::Execute_Prim(u32 op, u32 diff) {
|
||||
// This drives all drawing. All other state we just buffer up, then we apply it only
|
||||
// when it's time to draw. As most PSP games set state redundantly ALL THE TIME, this is a huge optimization.
|
||||
|
||||
u32 data = op & 0xFFFFFF;
|
||||
u32 count = data & 0xFFFF;
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
// Upper bits are ignored.
|
||||
GEPrimitiveType prim = static_cast<GEPrimitiveType>((data >> 16) & 7);
|
||||
SetDrawType(DRAW_PRIM, prim);
|
||||
|
||||
// Discard AA lines as we can't do anything that makes sense with these anyway. The SW plugin might, though.
|
||||
|
||||
if (gstate.isAntiAliasEnabled()) {
|
||||
// Discard AA lines in DOA
|
||||
if (prim == GE_PRIM_LINE_STRIP)
|
||||
return;
|
||||
// Discard AA lines in Summon Night 5
|
||||
if ((prim == GE_PRIM_LINES) && gstate.isSkinningEnabled())
|
||||
return;
|
||||
}
|
||||
|
||||
// This also makes skipping drawing very effective. This function can change the framebuffer.
|
||||
framebufferManagerGL_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason);
|
||||
if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) {
|
||||
drawEngine_.SetupVertexDecoder(gstate.vertType);
|
||||
// Rough estimate, not sure what's correct.
|
||||
cyclesExecuted += EstimatePerVertexCost() * count;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Memory::IsValidAddress(gstate_c.vertexAddr)) {
|
||||
ERROR_LOG_REPORT(G3D, "Bad vertex address %08x!", gstate_c.vertexAddr);
|
||||
return;
|
||||
}
|
||||
|
||||
void *verts = Memory::GetPointerUnchecked(gstate_c.vertexAddr);
|
||||
void *inds = 0;
|
||||
if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
|
||||
if (!Memory::IsValidAddress(gstate_c.indexAddr)) {
|
||||
ERROR_LOG_REPORT(G3D, "Bad index address %08x!", gstate_c.indexAddr);
|
||||
return;
|
||||
}
|
||||
inds = Memory::GetPointerUnchecked(gstate_c.indexAddr);
|
||||
}
|
||||
|
||||
#ifndef MOBILE_DEVICE
|
||||
if (prim > GE_PRIM_RECTANGLES) {
|
||||
ERROR_LOG_REPORT_ONCE(reportPrim, G3D, "Unexpected prim type: %d", prim);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gstate_c.dirty & DIRTY_VERTEXSHADER_STATE) {
|
||||
vertexCost_ = EstimatePerVertexCost();
|
||||
}
|
||||
gpuStats.vertexGPUCycles += vertexCost_ * count;
|
||||
cyclesExecuted += vertexCost_* count;
|
||||
|
||||
int bytesRead = 0;
|
||||
UpdateUVScaleOffset();
|
||||
drawEngine_.SubmitPrim(verts, inds, prim, count, gstate.vertType, &bytesRead);
|
||||
|
||||
// After drawing, we advance the vertexAddr (when non indexed) or indexAddr (when indexed).
|
||||
// Some games rely on this, they don't bother reloading VADDR and IADDR.
|
||||
// The VADDR/IADDR registers are NOT updated.
|
||||
AdvanceVerts(gstate.vertType, count, bytesRead);
|
||||
}
|
||||
|
||||
void GPU_GLES::Execute_LoadClut(u32 op, u32 diff) {
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);
|
||||
textureCacheGL_->LoadClut(gstate.getClutAddress(), gstate.getClutLoadBytes());
|
||||
}
|
||||
|
||||
void GPU_GLES::GetStats(char *buffer, size_t bufsize) {
|
||||
float vertexAverageCycles = gpuStats.numVertsSubmitted > 0 ? (float)gpuStats.vertexGPUCycles / (float)gpuStats.numVertsSubmitted : 0.0f;
|
||||
snprintf(buffer, bufsize - 1,
|
||||
@@ -762,8 +539,6 @@ void GPU_GLES::ClearShaderCache() {
|
||||
void GPU_GLES::CleanupBeforeUI() {
|
||||
// Clear any enabled vertex arrays.
|
||||
shaderManagerGL_->DirtyLastShader();
|
||||
glstate.arrayBuffer.bind(0);
|
||||
glstate.elementArrayBuffer.bind(0);
|
||||
}
|
||||
|
||||
void GPU_GLES::DoState(PointerWrap &p) {
|
||||
|
||||
+4
-28
@@ -36,7 +36,7 @@ public:
|
||||
~GPU_GLES();
|
||||
|
||||
// This gets called on startup and when we get back from settings.
|
||||
void CheckGPUFeatures();
|
||||
void CheckGPUFeatures() override;
|
||||
|
||||
bool IsReady() override;
|
||||
|
||||
@@ -55,34 +55,15 @@ public:
|
||||
|
||||
void ClearShaderCache() override;
|
||||
void CleanupBeforeUI() override;
|
||||
bool DecodeTexture(u8 *dest, const GPUgstate &state) override {
|
||||
return textureCacheGL_->DecodeTexture(dest, state);
|
||||
}
|
||||
bool FramebufferDirty() override;
|
||||
bool FramebufferReallyDirty() override;
|
||||
|
||||
void GetReportingInfo(std::string &primaryInfo, std::string &fullInfo) override {
|
||||
primaryInfo = reportingPrimaryInfo_;
|
||||
fullInfo = reportingFullInfo_;
|
||||
}
|
||||
|
||||
typedef void (GPU_GLES::*CmdFunc)(u32 op, u32 diff);
|
||||
struct CommandInfo {
|
||||
uint64_t flags;
|
||||
GPU_GLES::CmdFunc func;
|
||||
};
|
||||
|
||||
void Execute_Prim(u32 op, u32 diff);
|
||||
void Execute_LoadClut(u32 op, u32 diff);
|
||||
|
||||
// Using string because it's generic - makes no assumptions on the size of the shader IDs of this backend.
|
||||
std::vector<std::string> DebugGetShaderIDs(DebugShaderType shader) override;
|
||||
std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) override;
|
||||
|
||||
void BeginHostFrame() override;
|
||||
void EndHostFrame() override;
|
||||
|
||||
protected:
|
||||
void FastRunLoop(DisplayList &list) override;
|
||||
void FinishDeferred() override;
|
||||
|
||||
private:
|
||||
@@ -98,9 +79,6 @@ private:
|
||||
void Reinitialize() override;
|
||||
|
||||
inline void UpdateVsyncInterval(bool force);
|
||||
void UpdateCmdInfo();
|
||||
|
||||
static CommandInfo cmdInfo_[256];
|
||||
|
||||
FramebufferManagerGLES *framebufferManagerGL_;
|
||||
TextureCacheGLES *textureCacheGL_;
|
||||
@@ -109,12 +87,10 @@ private:
|
||||
FragmentTestCacheGLES fragmentTestCache_;
|
||||
ShaderManagerGLES *shaderManagerGL_;
|
||||
|
||||
std::string shaderCachePath_;
|
||||
|
||||
#ifdef _WIN32
|
||||
int lastVsync_;
|
||||
#endif
|
||||
int vertexCost_ = 0;
|
||||
|
||||
std::string reportingPrimaryInfo_;
|
||||
std::string reportingFullInfo_;
|
||||
std::string shaderCachePath_;
|
||||
};
|
||||
+164
-318
@@ -26,6 +26,8 @@
|
||||
#include "base/logging.h"
|
||||
#include "base/timeutil.h"
|
||||
#include "gfx/gl_debug_log.h"
|
||||
#include "gfx_es2/gpu_features.h"
|
||||
#include "thin3d/GLRenderManager.h"
|
||||
#include "i18n/i18n.h"
|
||||
#include "math/math_util.h"
|
||||
#include "math/lin/matrix4x4.h"
|
||||
@@ -35,17 +37,17 @@
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/System.h"
|
||||
#include "GPU/Math3D.h"
|
||||
#include "GPU/GPUState.h"
|
||||
#include "GPU/ge_constants.h"
|
||||
#include "ext/native/gfx/GLStateCache.h"
|
||||
#include "GPU/GLES/ShaderManagerGLES.h"
|
||||
#include "GPU/GLES/DrawEngineGLES.h"
|
||||
#include "GPU/Common/ShaderUniforms.h"
|
||||
#include "FramebufferManagerGLES.h"
|
||||
|
||||
Shader::Shader(const ShaderID &id, const char *code, uint32_t glShaderType, bool useHWTransform, uint32_t attrMask, uint64_t uniformMask)
|
||||
: failed_(false), useHWTransform_(useHWTransform), attrMask_(attrMask), uniformMask_(uniformMask) {
|
||||
Shader::Shader(GLRenderManager *render, const char *code, const std::string &desc, uint32_t glShaderType, bool useHWTransform, uint32_t attrMask, uint64_t uniformMask)
|
||||
: render_(render), failed_(false), useHWTransform_(useHWTransform), attrMask_(attrMask), uniformMask_(uniformMask) {
|
||||
PROFILE_THIS_SCOPE("shadercomp");
|
||||
isFragment_ = glShaderType == GL_FRAGMENT_SHADER;
|
||||
source_ = code;
|
||||
@@ -56,245 +58,141 @@ Shader::Shader(const ShaderID &id, const char *code, uint32_t glShaderType, bool
|
||||
printf("%s\n", code);
|
||||
#endif
|
||||
#endif
|
||||
shader = glCreateShader(glShaderType);
|
||||
glShaderSource(shader, 1, &code, 0);
|
||||
glCompileShader(shader);
|
||||
GLint success = 0;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
#define MAX_INFO_LOG_SIZE 2048
|
||||
GLchar infoLog[MAX_INFO_LOG_SIZE];
|
||||
GLsizei len;
|
||||
glGetShaderInfoLog(shader, MAX_INFO_LOG_SIZE, &len, infoLog);
|
||||
infoLog[len] = '\0';
|
||||
#ifdef __ANDROID__
|
||||
ELOG("Error in shader compilation! %s\n", infoLog);
|
||||
ELOG("Shader source:\n%s\n", (const char *)code);
|
||||
#endif
|
||||
|
||||
std::string desc = GetShaderString(SHADER_STRING_SHORT_DESC, id);
|
||||
ERROR_LOG(G3D, "Error in shader compilation for: %s", desc.c_str());
|
||||
ERROR_LOG(G3D, "Info log: %s", infoLog);
|
||||
ERROR_LOG(G3D, "Shader source:\n%s\n", (const char *)code);
|
||||
Reporting::ReportMessage("Error in shader compilation: info: %s\n%s\n%s", infoLog, desc.c_str(), (const char *)code);
|
||||
#ifdef SHADERLOG
|
||||
OutputDebugStringUTF8(infoLog);
|
||||
#endif
|
||||
failed_ = true;
|
||||
shader = 0;
|
||||
} else {
|
||||
DEBUG_LOG(G3D, "Compiled shader:\n%s\n", (const char *)code);
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
shader = render->CreateShader(glShaderType, source_, desc);
|
||||
}
|
||||
|
||||
Shader::~Shader() {
|
||||
if (shader)
|
||||
glDeleteShader(shader);
|
||||
render_->DeleteShader(shader);
|
||||
}
|
||||
|
||||
LinkedShader::LinkedShader(VShaderID VSID, Shader *vs, FShaderID FSID, Shader *fs, bool useHWTransform, bool preloading)
|
||||
: useHWTransform_(useHWTransform) {
|
||||
LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, FShaderID FSID, Shader *fs, bool useHWTransform, bool preloading)
|
||||
: render_(render), useHWTransform_(useHWTransform) {
|
||||
PROFILE_THIS_SCOPE("shaderlink");
|
||||
|
||||
program = glCreateProgram();
|
||||
vs_ = vs;
|
||||
glAttachShader(program, vs->shader);
|
||||
glAttachShader(program, fs->shader);
|
||||
|
||||
// Bind attribute locations to fixed locations so that they're
|
||||
// the same in all shaders. We use this later to minimize the calls to
|
||||
// glEnableVertexAttribArray and glDisableVertexAttribArray.
|
||||
glBindAttribLocation(program, ATTR_POSITION, "position");
|
||||
glBindAttribLocation(program, ATTR_TEXCOORD, "texcoord");
|
||||
glBindAttribLocation(program, ATTR_NORMAL, "normal");
|
||||
glBindAttribLocation(program, ATTR_W1, "w1");
|
||||
glBindAttribLocation(program, ATTR_W2, "w2");
|
||||
glBindAttribLocation(program, ATTR_COLOR0, "color0");
|
||||
glBindAttribLocation(program, ATTR_COLOR1, "color1");
|
||||
std::vector<GLRShader *> shaders;
|
||||
shaders.push_back(vs->shader);
|
||||
shaders.push_back(fs->shader);
|
||||
|
||||
#if !defined(USING_GLES2)
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_DUALSOURCE_BLEND)) {
|
||||
// Dual source alpha
|
||||
glBindFragDataLocationIndexed(program, 0, 0, "fragColor0");
|
||||
glBindFragDataLocationIndexed(program, 0, 1, "fragColor1");
|
||||
} else if (gl_extensions.VersionGEThan(3, 3, 0)) {
|
||||
glBindFragDataLocation(program, 0, "fragColor0");
|
||||
}
|
||||
#elif !defined(IOS)
|
||||
if (gl_extensions.GLES3) {
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_DUALSOURCE_BLEND)) {
|
||||
glBindFragDataLocationIndexedEXT(program, 0, 0, "fragColor0");
|
||||
glBindFragDataLocationIndexedEXT(program, 0, 1, "fragColor1");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
glLinkProgram(program);
|
||||
std::vector<GLRProgram::Semantic> semantics;
|
||||
semantics.push_back({ ATTR_POSITION, "position" });
|
||||
semantics.push_back({ ATTR_TEXCOORD, "texcoord" });
|
||||
semantics.push_back({ ATTR_NORMAL, "normal" });
|
||||
semantics.push_back({ ATTR_COLOR0, "color0" });
|
||||
semantics.push_back({ ATTR_COLOR1, "color1" });
|
||||
|
||||
GLint linkStatus = GL_FALSE;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
|
||||
if (linkStatus != GL_TRUE) {
|
||||
GLint bufLength = 0;
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
|
||||
if (bufLength) {
|
||||
char* buf = new char[bufLength];
|
||||
glGetProgramInfoLog(program, bufLength, NULL, buf);
|
||||
#ifdef __ANDROID__
|
||||
ELOG("Could not link program:\n %s", buf);
|
||||
#endif
|
||||
ERROR_LOG(G3D, "Could not link program:\n %s", buf);
|
||||
std::vector<GLRProgram::UniformLocQuery> queries;
|
||||
queries.push_back({ &u_tex, "tex" });
|
||||
queries.push_back({ &u_proj, "u_proj" });
|
||||
queries.push_back({ &u_proj_through, "u_proj_through" });
|
||||
|
||||
std::string vs_desc = vs->GetShaderString(SHADER_STRING_SHORT_DESC, VSID);
|
||||
std::string fs_desc = fs->GetShaderString(SHADER_STRING_SHORT_DESC, FSID);
|
||||
std::string vs_source = vs->GetShaderString(SHADER_STRING_SOURCE_CODE, VSID);
|
||||
std::string fs_source = fs->GetShaderString(SHADER_STRING_SOURCE_CODE, FSID);
|
||||
queries.push_back({ &u_proj, "u_proj" });
|
||||
queries.push_back({ &u_proj_through, "u_proj_through" });
|
||||
queries.push_back({ &u_texenv, "u_texenv" });
|
||||
queries.push_back({ &u_fogcolor, "u_fogcolor" });
|
||||
queries.push_back({ &u_fogcoef, "u_fogcoef" });
|
||||
queries.push_back({ &u_alphacolorref, "u_alphacolorref" });
|
||||
queries.push_back({ &u_alphacolormask, "u_alphacolormask" });
|
||||
queries.push_back({ &u_stencilReplaceValue, "u_stencilReplaceValue" });
|
||||
queries.push_back({ &u_testtex, "testtex" });
|
||||
|
||||
ERROR_LOG(G3D, "VS desc:\n%s", vs_desc.c_str());
|
||||
ERROR_LOG(G3D, "FS desc:\n%s", fs_desc.c_str());
|
||||
ERROR_LOG(G3D, "VS:\n%s\n", vs_source.c_str());
|
||||
ERROR_LOG(G3D, "FS:\n%s\n", fs_source.c_str());
|
||||
if (preloading) {
|
||||
Reporting::ReportMessage("Error in shader program link during preload: info: %s\nfs: %s\n%s\nvs: %s\n%s", buf, fs_desc.c_str(), fs_source.c_str(), vs_desc.c_str(), vs_source.c_str());
|
||||
} else {
|
||||
Reporting::ReportMessage("Error in shader program link: info: %s\nfs: %s\n%s\nvs: %s\n%s", buf, fs_desc.c_str(), fs_source.c_str(), vs_desc.c_str(), vs_source.c_str());
|
||||
}
|
||||
#ifdef SHADERLOG
|
||||
OutputDebugStringUTF8(buf);
|
||||
OutputDebugStringUTF8(vs_source.c_str());
|
||||
OutputDebugStringUTF8(fs_source.c_str());
|
||||
#endif
|
||||
delete [] buf; // we're dead!
|
||||
}
|
||||
// Prevent a buffer overflow.
|
||||
numBones = 0;
|
||||
// Avoid weird attribute enables.
|
||||
attrMask = 0;
|
||||
availableUniforms = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
INFO_LOG(G3D, "Linked shader: vs %i fs %i", (int)vs->shader, (int)fs->shader);
|
||||
|
||||
u_tex = glGetUniformLocation(program, "tex");
|
||||
u_proj = glGetUniformLocation(program, "u_proj");
|
||||
u_proj_through = glGetUniformLocation(program, "u_proj_through");
|
||||
u_texenv = glGetUniformLocation(program, "u_texenv");
|
||||
u_fogcolor = glGetUniformLocation(program, "u_fogcolor");
|
||||
u_fogcoef = glGetUniformLocation(program, "u_fogcoef");
|
||||
u_alphacolorref = glGetUniformLocation(program, "u_alphacolorref");
|
||||
u_alphacolormask = glGetUniformLocation(program, "u_alphacolormask");
|
||||
u_stencilReplaceValue = glGetUniformLocation(program, "u_stencilReplaceValue");
|
||||
u_testtex = glGetUniformLocation(program, "testtex");
|
||||
|
||||
u_fbotex = glGetUniformLocation(program, "fbotex");
|
||||
u_blendFixA = glGetUniformLocation(program, "u_blendFixA");
|
||||
u_blendFixB = glGetUniformLocation(program, "u_blendFixB");
|
||||
u_fbotexSize = glGetUniformLocation(program, "u_fbotexSize");
|
||||
queries.push_back({ &u_fbotex, "fbotex" });
|
||||
queries.push_back({ &u_blendFixA, "u_blendFixA" });
|
||||
queries.push_back({ &u_blendFixB, "u_blendFixB" });
|
||||
queries.push_back({ &u_fbotexSize, "u_fbotexSize" });
|
||||
|
||||
// Transform
|
||||
u_view = glGetUniformLocation(program, "u_view");
|
||||
u_world = glGetUniformLocation(program, "u_world");
|
||||
u_texmtx = glGetUniformLocation(program, "u_texmtx");
|
||||
if (VSID.Bit(VS_BIT_ENABLE_BONES))
|
||||
numBones = TranslateNumBones(VSID.Bits(VS_BIT_BONES, 3) + 1);
|
||||
else
|
||||
numBones = 0;
|
||||
u_depthRange = glGetUniformLocation(program, "u_depthRange");
|
||||
queries.push_back({ &u_view, "u_view" });
|
||||
queries.push_back({ &u_world, "u_world" });
|
||||
queries.push_back({ &u_texmtx, "u_texmtx" });
|
||||
|
||||
#ifdef USE_BONE_ARRAY
|
||||
u_bone = glGetUniformLocation(program, "u_bone");
|
||||
#else
|
||||
for (int i = 0; i < 8; i++) {
|
||||
char name[10];
|
||||
sprintf(name, "u_bone%i", i);
|
||||
u_bone[i] = glGetUniformLocation(program, name);
|
||||
}
|
||||
#endif
|
||||
queries.push_back({ &u_depthRange, "u_depthRange" });
|
||||
|
||||
// Lighting, texturing
|
||||
u_ambient = glGetUniformLocation(program, "u_ambient");
|
||||
u_matambientalpha = glGetUniformLocation(program, "u_matambientalpha");
|
||||
u_matdiffuse = glGetUniformLocation(program, "u_matdiffuse");
|
||||
u_matspecular = glGetUniformLocation(program, "u_matspecular");
|
||||
u_matemissive = glGetUniformLocation(program, "u_matemissive");
|
||||
u_uvscaleoffset = glGetUniformLocation(program, "u_uvscaleoffset");
|
||||
u_texclamp = glGetUniformLocation(program, "u_texclamp");
|
||||
u_texclampoff = glGetUniformLocation(program, "u_texclampoff");
|
||||
u_guardband = glGetUniformLocation(program, "u_guardband");
|
||||
queries.push_back({ &u_ambient, "u_ambient" });
|
||||
queries.push_back({ &u_matambientalpha, "u_matambientalpha" });
|
||||
queries.push_back({ &u_matdiffuse, "u_matdiffuse" });
|
||||
queries.push_back({ &u_matspecular, "u_matspecular" });
|
||||
queries.push_back({ &u_matemissive, "u_matemissive" });
|
||||
queries.push_back({ &u_uvscaleoffset, "u_uvscaleoffset" });
|
||||
queries.push_back({ &u_texclamp, "u_texclamp" });
|
||||
queries.push_back({ &u_texclampoff, "u_texclampoff" });
|
||||
queries.push_back({ &u_guardband, "u_guardband" });
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
char temp[64];
|
||||
sprintf(temp, "u_lightpos%i", i);
|
||||
u_lightpos[i] = glGetUniformLocation(program, temp);
|
||||
sprintf(temp, "u_lightdir%i", i);
|
||||
u_lightdir[i] = glGetUniformLocation(program, temp);
|
||||
sprintf(temp, "u_lightatt%i", i);
|
||||
u_lightatt[i] = glGetUniformLocation(program, temp);
|
||||
sprintf(temp, "u_lightangle%i", i);
|
||||
u_lightangle[i] = glGetUniformLocation(program, temp);
|
||||
sprintf(temp, "u_lightspotCoef%i", i);
|
||||
u_lightspotCoef[i] = glGetUniformLocation(program, temp);
|
||||
sprintf(temp, "u_lightambient%i", i);
|
||||
u_lightambient[i] = glGetUniformLocation(program, temp);
|
||||
sprintf(temp, "u_lightdiffuse%i", i);
|
||||
u_lightdiffuse[i] = glGetUniformLocation(program, temp);
|
||||
sprintf(temp, "u_lightspecular%i", i);
|
||||
u_lightspecular[i] = glGetUniformLocation(program, temp);
|
||||
static const char * const lightPosNames[4] = { "u_lightpos0", "u_lightpos1", "u_lightpos2", "u_lightpos3", };
|
||||
queries.push_back({ &u_lightpos[i], lightPosNames[i] });
|
||||
static const char * const lightdir_names[4] = { "u_lightdir0", "u_lightdir1", "u_lightdir2", "u_lightdir3", };
|
||||
queries.push_back({ &u_lightdir[i], lightdir_names[i] });
|
||||
static const char * const lightatt_names[4] = { "u_lightatt0", "u_lightatt1", "u_lightatt2", "u_lightatt3", };
|
||||
queries.push_back({ &u_lightatt[i], lightatt_names[i] });
|
||||
static const char * const lightangle_spotCoef_names[4] = { "u_lightangle_spotCoef0", "u_lightangle_spotCoef1", "u_lightangle_spotCoef2", "u_lightangle_spotCoef3", };
|
||||
queries.push_back({ &u_lightangle_spotCoef[i], lightangle_spotCoef_names[i] });
|
||||
|
||||
static const char * const lightambient_names[4] = { "u_lightambient0", "u_lightambient1", "u_lightambient2", "u_lightambient3", };
|
||||
queries.push_back({ &u_lightambient[i], lightambient_names[i] });
|
||||
static const char * const lightdiffuse_names[4] = { "u_lightdiffuse0", "u_lightdiffuse1", "u_lightdiffuse2", "u_lightdiffuse3", };
|
||||
queries.push_back({ &u_lightdiffuse[i], lightdiffuse_names[i] });
|
||||
static const char * const lightspecular_names[4] = { "u_lightspecular0", "u_lightspecular1", "u_lightspecular2", "u_lightspecular3", };
|
||||
queries.push_back({ &u_lightspecular[i], lightspecular_names[i] });
|
||||
}
|
||||
|
||||
// We need to fetch these unconditionally, gstate_c.spline or bezier will not be set if we
|
||||
// create this shader at load time from the shader cache.
|
||||
u_tess_pos_tex = glGetUniformLocation(program, "u_tess_pos_tex");
|
||||
u_tess_tex_tex = glGetUniformLocation(program, "u_tess_tex_tex");
|
||||
u_tess_col_tex = glGetUniformLocation(program, "u_tess_col_tex");
|
||||
u_spline_count_u = glGetUniformLocation(program, "u_spline_count_u");
|
||||
u_spline_count_v = glGetUniformLocation(program, "u_spline_count_v");
|
||||
u_spline_type_u = glGetUniformLocation(program, "u_spline_type_u");
|
||||
u_spline_type_v = glGetUniformLocation(program, "u_spline_type_v");
|
||||
queries.push_back({ &u_tess_pos_tex, "u_tess_pos_tex" });
|
||||
queries.push_back({ &u_tess_tex_tex, "u_tess_tex_tex" });
|
||||
queries.push_back({ &u_tess_col_tex, "u_tess_col_tex" });
|
||||
queries.push_back({ &u_spline_count_u, "u_spline_count_u" });
|
||||
queries.push_back({ &u_spline_count_v, "u_spline_count_v" });
|
||||
queries.push_back({ &u_spline_type_u, "u_spline_type_u" });
|
||||
queries.push_back({ &u_spline_type_v, "u_spline_type_v" });
|
||||
|
||||
attrMask = vs->GetAttrMask();
|
||||
availableUniforms = vs->GetUniformMask() | fs->GetUniformMask();
|
||||
|
||||
glUseProgram(program);
|
||||
std::vector<GLRProgram::Initializer> initialize;
|
||||
initialize.push_back({ &u_tex, 0, 0 });
|
||||
initialize.push_back({ &u_fbotex, 0, 1 });
|
||||
initialize.push_back({ &u_testtex, 0, 2 });
|
||||
initialize.push_back({ &u_tess_pos_tex, 0, 4 }); // Texture unit 4
|
||||
initialize.push_back({ &u_tess_tex_tex, 0, 5 }); // Texture unit 5
|
||||
initialize.push_back({ &u_tess_col_tex, 0, 6 }); // Texture unit 6
|
||||
|
||||
// Default uniform values
|
||||
glUniform1i(u_tex, 0);
|
||||
glUniform1i(u_fbotex, 1);
|
||||
glUniform1i(u_testtex, 2);
|
||||
|
||||
if (u_tess_pos_tex != -1)
|
||||
glUniform1i(u_tess_pos_tex, 4); // Texture unit 4
|
||||
if (u_tess_tex_tex != -1)
|
||||
glUniform1i(u_tess_tex_tex, 5); // Texture unit 5
|
||||
if (u_tess_col_tex != -1)
|
||||
glUniform1i(u_tess_col_tex, 6); // Texture unit 6
|
||||
program = render->CreateProgram(shaders, semantics, queries, initialize, gstate_c.featureFlags & GPU_SUPPORTS_DUALSOURCE_BLEND);
|
||||
|
||||
// The rest, use the "dirty" mechanism.
|
||||
dirtyUniforms = DIRTY_ALL_UNIFORMS;
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
LinkedShader::~LinkedShader() {
|
||||
// Shaders are automatically detached by glDeleteProgram.
|
||||
glDeleteProgram(program);
|
||||
render_->DeleteProgram(program);
|
||||
}
|
||||
|
||||
// Utility
|
||||
static void SetColorUniform3(int uniform, u32 color) {
|
||||
float f[4];
|
||||
Uint8x4ToFloat4(f, color);
|
||||
glUniform3fv(uniform, 1, f);
|
||||
static inline void SetFloatUniform(GLRenderManager *render, GLint *uniform, float value) {
|
||||
render->SetUniformF(uniform, 1, &value);
|
||||
}
|
||||
|
||||
static void SetColorUniform3Alpha(int uniform, u32 color, u8 alpha) {
|
||||
static inline void SetFloatUniform2(GLRenderManager *render, GLint *uniform, float value[2]) {
|
||||
render->SetUniformF(uniform, 2, value);
|
||||
}
|
||||
|
||||
static inline void SetColorUniform3(GLRenderManager *render, GLint *uniform, u32 color) {
|
||||
float f[4];
|
||||
Uint8x4ToFloat4(f, color);
|
||||
render->SetUniformF(uniform, 3, f);
|
||||
}
|
||||
|
||||
static void SetColorUniform3Alpha(GLRenderManager *render, GLint *uniform, u32 color, u8 alpha) {
|
||||
float f[4];
|
||||
Uint8x3ToFloat4_AlphaUint8(f, color, alpha);
|
||||
glUniform4fv(uniform, 1, f);
|
||||
render->SetUniformF(uniform, 4, f);
|
||||
}
|
||||
|
||||
// This passes colors unscaled (e.g. 0 - 255 not 0 - 1.)
|
||||
static void SetColorUniform3Alpha255(int uniform, u32 color, u8 alpha) {
|
||||
static void SetColorUniform3Alpha255(GLRenderManager *render, GLint *uniform, u32 color, u8 alpha) {
|
||||
if (gl_extensions.gpuVendor == GPU_VENDOR_IMGTEC) {
|
||||
const float col[4] = {
|
||||
(float)((color & 0xFF) >> 0) * (1.0f / 255.0f),
|
||||
@@ -302,7 +200,7 @@ static void SetColorUniform3Alpha255(int uniform, u32 color, u8 alpha) {
|
||||
(float)((color & 0xFF0000) >> 16) * (1.0f / 255.0f),
|
||||
(float)alpha * (1.0f / 255.0f)
|
||||
};
|
||||
glUniform4fv(uniform, 1, col);
|
||||
render->SetUniformF(uniform, 4, col);
|
||||
} else {
|
||||
const float col[4] = {
|
||||
(float)((color & 0xFF) >> 0),
|
||||
@@ -310,44 +208,44 @@ static void SetColorUniform3Alpha255(int uniform, u32 color, u8 alpha) {
|
||||
(float)((color & 0xFF0000) >> 16),
|
||||
(float)alpha
|
||||
};
|
||||
glUniform4fv(uniform, 1, col);
|
||||
render->SetUniformF(uniform, 4, col);
|
||||
}
|
||||
}
|
||||
|
||||
static void SetColorUniform3iAlpha(int uniform, u32 color, u8 alpha) {
|
||||
static void SetColorUniform3iAlpha(GLRenderManager *render, GLint *uniform, u32 color, u8 alpha) {
|
||||
const int col[4] = {
|
||||
(int)((color & 0xFF) >> 0),
|
||||
(int)((color & 0xFF00) >> 8),
|
||||
(int)((color & 0xFF0000) >> 16),
|
||||
(int)alpha,
|
||||
};
|
||||
glUniform4iv(uniform, 1, col);
|
||||
render->SetUniformI(uniform, 4, col);
|
||||
}
|
||||
|
||||
static void SetColorUniform3ExtraFloat(int uniform, u32 color, float extra) {
|
||||
static void SetColorUniform3ExtraFloat(GLRenderManager *render, GLint *uniform, u32 color, float extra) {
|
||||
const float col[4] = {
|
||||
((color & 0xFF)) / 255.0f,
|
||||
((color & 0xFF00) >> 8) / 255.0f,
|
||||
((color & 0xFF0000) >> 16) / 255.0f,
|
||||
extra
|
||||
};
|
||||
glUniform4fv(uniform, 1, col);
|
||||
render->SetUniformF(uniform, 4, col);
|
||||
}
|
||||
|
||||
static void SetFloat24Uniform3(int uniform, const uint32_t data[3]) {
|
||||
static void SetFloat24Uniform3(GLRenderManager *render, GLint *uniform, const uint32_t data[3]) {
|
||||
float f[4];
|
||||
ExpandFloat24x3ToFloat4(f, data);
|
||||
glUniform3fv(uniform, 1, f);
|
||||
render->SetUniformF(uniform, 3, f);
|
||||
}
|
||||
|
||||
static void SetFloatUniform4(int uniform, float data[4]) {
|
||||
glUniform4fv(uniform, 1, data);
|
||||
static void SetFloatUniform4(GLRenderManager *render, GLint *uniform, float data[4]) {
|
||||
render->SetUniformF(uniform, 4, data);
|
||||
}
|
||||
|
||||
static void SetMatrix4x3(int uniform, const float *m4x3) {
|
||||
static void SetMatrix4x3(GLRenderManager *render, GLint *uniform, const float *m4x3) {
|
||||
float m4x4[16];
|
||||
ConvertMatrix4x3To4x4(m4x4, m4x3);
|
||||
glUniformMatrix4fv(uniform, 1, GL_FALSE, m4x4);
|
||||
render->SetUniformM4x4(uniform, m4x4);
|
||||
}
|
||||
|
||||
static inline void ScaleProjMatrix(Matrix4x4 &in) {
|
||||
@@ -361,34 +259,12 @@ static inline void ScaleProjMatrix(Matrix4x4 &in) {
|
||||
in.translateAndScale(trans, scale);
|
||||
}
|
||||
|
||||
void LinkedShader::use(const VShaderID &VSID, LinkedShader *previous) {
|
||||
glUseProgram(program);
|
||||
int enable, disable;
|
||||
if (previous) {
|
||||
enable = attrMask & ~previous->attrMask;
|
||||
disable = (~attrMask) & previous->attrMask;
|
||||
} else {
|
||||
enable = attrMask;
|
||||
disable = ~attrMask;
|
||||
}
|
||||
for (int i = 0; i < ATTR_COUNT; i++) {
|
||||
if (enable & (1 << i))
|
||||
glEnableVertexAttribArray(i);
|
||||
else if (disable & (1 << i))
|
||||
glDisableVertexAttribArray(i);
|
||||
}
|
||||
void LinkedShader::use(const ShaderID &VSID) {
|
||||
render_->BindProgram(program);
|
||||
// Note that we no longer track attr masks here - we do it for the input layouts instead.
|
||||
}
|
||||
|
||||
void LinkedShader::stop() {
|
||||
for (int i = 0; i < ATTR_COUNT; i++) {
|
||||
if (attrMask & (1 << i))
|
||||
glDisableVertexAttribArray(i);
|
||||
}
|
||||
}
|
||||
|
||||
void LinkedShader::UpdateUniforms(u32 vertType, const VShaderID &vsid) {
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
|
||||
void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid) {
|
||||
u64 dirty = dirtyUniforms & availableUniforms;
|
||||
dirtyUniforms = 0;
|
||||
if (!dirty)
|
||||
@@ -450,7 +326,7 @@ void LinkedShader::UpdateUniforms(u32 vertType, const VShaderID &vsid) {
|
||||
|
||||
ScaleProjMatrix(flippedMatrix);
|
||||
|
||||
glUniformMatrix4fv(u_proj, 1, GL_FALSE, flippedMatrix.m);
|
||||
render_->SetUniformM4x4(&u_proj, flippedMatrix.m);
|
||||
}
|
||||
if (dirty & DIRTY_PROJTHROUGHMATRIX)
|
||||
{
|
||||
@@ -461,19 +337,19 @@ void LinkedShader::UpdateUniforms(u32 vertType, const VShaderID &vsid) {
|
||||
} else {
|
||||
proj_through.setOrtho(0.0f, gstate_c.curRTWidth, gstate_c.curRTHeight, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
glUniformMatrix4fv(u_proj_through, 1, GL_FALSE, proj_through.getReadPtr());
|
||||
render_->SetUniformM4x4(&u_proj_through, proj_through.getReadPtr());
|
||||
}
|
||||
if (dirty & DIRTY_TEXENV) {
|
||||
SetColorUniform3(u_texenv, gstate.texenvcolor);
|
||||
SetColorUniform3(render_, &u_texenv, gstate.texenvcolor);
|
||||
}
|
||||
if (dirty & DIRTY_ALPHACOLORREF) {
|
||||
SetColorUniform3Alpha255(u_alphacolorref, gstate.getColorTestRef(), gstate.getAlphaTestRef() & gstate.getAlphaTestMask());
|
||||
SetColorUniform3Alpha255(render_, &u_alphacolorref, gstate.getColorTestRef(), gstate.getAlphaTestRef() & gstate.getAlphaTestMask());
|
||||
}
|
||||
if (dirty & DIRTY_ALPHACOLORMASK) {
|
||||
SetColorUniform3iAlpha(u_alphacolormask, gstate.colortestmask, gstate.getAlphaTestMask());
|
||||
SetColorUniform3iAlpha(render_, &u_alphacolormask, gstate.colortestmask, gstate.getAlphaTestMask());
|
||||
}
|
||||
if (dirty & DIRTY_FOGCOLOR) {
|
||||
SetColorUniform3(u_fogcolor, gstate.fogcolor);
|
||||
SetColorUniform3(render_, &u_fogcolor, gstate.fogcolor);
|
||||
}
|
||||
if (dirty & DIRTY_FOGCOEF) {
|
||||
float fogcoef[2] = {
|
||||
@@ -496,7 +372,7 @@ void LinkedShader::UpdateUniforms(u32 vertType, const VShaderID &vsid) {
|
||||
ERROR_LOG_REPORT_ONCE(fognan, G3D, "Unhandled fog NaN/INF combo: %f %f", fogcoef[0], fogcoef[1]);
|
||||
}
|
||||
#endif
|
||||
glUniform2fv(u_fogcoef, 1, fogcoef);
|
||||
render_->SetUniformF(&u_fogcoef, 2, fogcoef);
|
||||
}
|
||||
|
||||
if (dirty & DIRTY_UVSCALEOFFSET) {
|
||||
@@ -520,7 +396,7 @@ void LinkedShader::UpdateUniforms(u32 vertType, const VShaderID &vsid) {
|
||||
uvscaleoff[2] = 0.0f;
|
||||
uvscaleoff[3] = 0.0f;
|
||||
}
|
||||
glUniform4fv(u_uvscaleoffset, 1, uvscaleoff);
|
||||
render_->SetUniformF(&u_uvscaleoffset, 4, uvscaleoff);
|
||||
}
|
||||
|
||||
if ((dirty & DIRTY_TEXCLAMP) && u_texclamp != -1) {
|
||||
@@ -542,27 +418,27 @@ void LinkedShader::UpdateUniforms(u32 vertType, const VShaderID &vsid) {
|
||||
gstate_c.curTextureXOffset * invW,
|
||||
gstate_c.curTextureYOffset * invH,
|
||||
};
|
||||
glUniform4fv(u_texclamp, 1, texclamp);
|
||||
render_->SetUniformF(&u_texclamp, 4, texclamp);
|
||||
if (u_texclampoff != -1) {
|
||||
glUniform2fv(u_texclampoff, 1, texclampoff);
|
||||
render_->SetUniformF(&u_texclampoff, 2, texclampoff);
|
||||
}
|
||||
}
|
||||
|
||||
// Transform
|
||||
if (dirty & DIRTY_WORLDMATRIX) {
|
||||
SetMatrix4x3(u_world, gstate.worldMatrix);
|
||||
SetMatrix4x3(render_, &u_world, gstate.worldMatrix);
|
||||
}
|
||||
if (dirty & DIRTY_VIEWMATRIX) {
|
||||
SetMatrix4x3(u_view, gstate.viewMatrix);
|
||||
SetMatrix4x3(render_, &u_view, gstate.viewMatrix);
|
||||
}
|
||||
if (dirty & DIRTY_TEXMATRIX) {
|
||||
SetMatrix4x3(u_texmtx, gstate.tgenMatrix);
|
||||
SetMatrix4x3(render_, &u_texmtx, gstate.tgenMatrix);
|
||||
}
|
||||
|
||||
if (dirty & DIRTY_GUARDBAND) {
|
||||
float gb[4];
|
||||
ComputeGuardband(gb, 0.0f);
|
||||
SetFloatUniform4(u_guardband, gb);
|
||||
SetFloatUniform4(render_, &u_guardband, gb);
|
||||
}
|
||||
|
||||
if ((dirty & DIRTY_DEPTHRANGE) && u_depthRange != -1) {
|
||||
@@ -589,53 +465,20 @@ void LinkedShader::UpdateUniforms(u32 vertType, const VShaderID &vsid) {
|
||||
}
|
||||
|
||||
float data[4] = { viewZScale, viewZCenter, viewZCenter, viewZInvScale };
|
||||
SetFloatUniform4(u_depthRange, data);
|
||||
SetFloatUniform4(render_, &u_depthRange, data);
|
||||
}
|
||||
|
||||
if (dirty & DIRTY_STENCILREPLACEVALUE) {
|
||||
glUniform1f(u_stencilReplaceValue, (float)gstate.getStencilTestRef() * (1.0f / 255.0f));
|
||||
float f = (float)gstate.getStencilTestRef() * (1.0f / 255.0f);
|
||||
render_->SetUniformF(&u_stencilReplaceValue, 1, &f);
|
||||
}
|
||||
// TODO: Could even set all bones in one go if they're all dirty.
|
||||
#ifdef USE_BONE_ARRAY
|
||||
if (u_bone != -1) {
|
||||
float allBones[8 * 16];
|
||||
|
||||
bool allDirty = true;
|
||||
for (int i = 0; i < numBones; i++) {
|
||||
if (dirty & (DIRTY_BONEMATRIX0 << i)) {
|
||||
ConvertMatrix4x3To4x4(allBones + 16 * i, gstate.boneMatrix + 12 * i);
|
||||
} else {
|
||||
allDirty = false;
|
||||
}
|
||||
}
|
||||
if (allDirty) {
|
||||
// Set them all with one call
|
||||
glUniformMatrix4fv(u_bone, numBones, GL_FALSE, allBones);
|
||||
} else {
|
||||
// Set them one by one. Could try to coalesce two in a row etc but too lazy.
|
||||
for (int i = 0; i < numBones; i++) {
|
||||
if (dirty & (DIRTY_BONEMATRIX0 << i)) {
|
||||
glUniformMatrix4fv(u_bone + i, 1, GL_FALSE, allBones + 16 * i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
float bonetemp[16];
|
||||
for (int i = 0; i < numBones; i++) {
|
||||
if (dirty & (DIRTY_BONEMATRIX0 << i)) {
|
||||
ConvertMatrix4x3To4x4(bonetemp, gstate.boneMatrix + 12 * i);
|
||||
glUniformMatrix4fv(u_bone[i], 1, GL_FALSE, bonetemp);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (dirty & DIRTY_SHADERBLEND) {
|
||||
if (u_blendFixA != -1) {
|
||||
SetColorUniform3(u_blendFixA, gstate.getFixA());
|
||||
SetColorUniform3(render_, &u_blendFixA, gstate.getFixA());
|
||||
}
|
||||
if (u_blendFixB != -1) {
|
||||
SetColorUniform3(u_blendFixB, gstate.getFixB());
|
||||
SetColorUniform3(render_, &u_blendFixB, gstate.getFixB());
|
||||
}
|
||||
|
||||
const float fbotexSize[2] = {
|
||||
@@ -643,25 +486,25 @@ void LinkedShader::UpdateUniforms(u32 vertType, const VShaderID &vsid) {
|
||||
1.0f / (float)gstate_c.curRTRenderHeight,
|
||||
};
|
||||
if (u_fbotexSize != -1) {
|
||||
glUniform2fv(u_fbotexSize, 1, fbotexSize);
|
||||
render_->SetUniformF(&u_fbotexSize, 2, fbotexSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Lighting
|
||||
if (dirty & DIRTY_AMBIENT) {
|
||||
SetColorUniform3Alpha(u_ambient, gstate.ambientcolor, gstate.getAmbientA());
|
||||
SetColorUniform3Alpha(render_, &u_ambient, gstate.ambientcolor, gstate.getAmbientA());
|
||||
}
|
||||
if (dirty & DIRTY_MATAMBIENTALPHA) {
|
||||
SetColorUniform3Alpha(u_matambientalpha, gstate.materialambient, gstate.getMaterialAmbientA());
|
||||
SetColorUniform3Alpha(render_, &u_matambientalpha, gstate.materialambient, gstate.getMaterialAmbientA());
|
||||
}
|
||||
if (dirty & DIRTY_MATDIFFUSE) {
|
||||
SetColorUniform3(u_matdiffuse, gstate.materialdiffuse);
|
||||
SetColorUniform3(render_, &u_matdiffuse, gstate.materialdiffuse);
|
||||
}
|
||||
if (dirty & DIRTY_MATEMISSIVE) {
|
||||
SetColorUniform3(u_matemissive, gstate.materialemissive);
|
||||
SetColorUniform3(render_, &u_matemissive, gstate.materialemissive);
|
||||
}
|
||||
if (dirty & DIRTY_MATSPECULAR) {
|
||||
SetColorUniform3ExtraFloat(u_matspecular, gstate.materialspecular, getFloat24(gstate.materialspecularcoef));
|
||||
SetColorUniform3ExtraFloat(render_, &u_matspecular, gstate.materialspecular, getFloat24(gstate.materialspecularcoef));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
@@ -677,34 +520,35 @@ void LinkedShader::UpdateUniforms(u32 vertType, const VShaderID &vsid) {
|
||||
else
|
||||
len = 1.0f / len;
|
||||
float vec[3] = { x * len, y * len, z * len };
|
||||
glUniform3fv(u_lightpos[i], 1, vec);
|
||||
render_->SetUniformF(&u_lightpos[i], 3, vec);
|
||||
} else {
|
||||
SetFloat24Uniform3(u_lightpos[i], &gstate.lpos[i * 3]);
|
||||
SetFloat24Uniform3(render_, &u_lightpos[i], &gstate.lpos[i * 3]);
|
||||
}
|
||||
if (u_lightdir[i] != -1) SetFloat24Uniform3(u_lightdir[i], &gstate.ldir[i * 3]);
|
||||
if (u_lightatt[i] != -1) SetFloat24Uniform3(u_lightatt[i], &gstate.latt[i * 3]);
|
||||
if (u_lightangle[i] != -1) glUniform1f(u_lightangle[i], getFloat24(gstate.lcutoff[i]));
|
||||
if (u_lightspotCoef[i] != -1) glUniform1f(u_lightspotCoef[i], getFloat24(gstate.lconv[i]));
|
||||
if (u_lightambient[i] != -1) SetColorUniform3(u_lightambient[i], gstate.lcolor[i * 3]);
|
||||
if (u_lightdiffuse[i] != -1) SetColorUniform3(u_lightdiffuse[i], gstate.lcolor[i * 3 + 1]);
|
||||
if (u_lightspecular[i] != -1) SetColorUniform3(u_lightspecular[i], gstate.lcolor[i * 3 + 2]);
|
||||
if (u_lightdir[i] != -1) SetFloat24Uniform3(render_, &u_lightdir[i], &gstate.ldir[i * 3]);
|
||||
if (u_lightatt[i] != -1) SetFloat24Uniform3(render_, &u_lightatt[i], &gstate.latt[i * 3]);
|
||||
if (u_lightangle_spotCoef[i] != -1) {
|
||||
float lightangle_spotCoef[2] = { getFloat24(gstate.lcutoff[i]), getFloat24(gstate.lconv[i]) };
|
||||
SetFloatUniform2(render_, &u_lightangle_spotCoef[i], lightangle_spotCoef);
|
||||
}
|
||||
if (u_lightambient[i] != -1) SetColorUniform3(render_, &u_lightambient[i], gstate.lcolor[i * 3]);
|
||||
if (u_lightdiffuse[i] != -1) SetColorUniform3(render_, &u_lightdiffuse[i], gstate.lcolor[i * 3 + 1]);
|
||||
if (u_lightspecular[i] != -1) SetColorUniform3(render_, &u_lightspecular[i], gstate.lcolor[i * 3 + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty & DIRTY_BEZIERSPLINE) {
|
||||
glUniform1i(u_spline_count_u, gstate_c.spline_count_u);
|
||||
render_->SetUniformI1(&u_spline_count_u, gstate_c.spline_count_u);
|
||||
if (u_spline_count_v != -1)
|
||||
glUniform1i(u_spline_count_v, gstate_c.spline_count_v);
|
||||
render_->SetUniformI1(&u_spline_count_v, gstate_c.spline_count_v);
|
||||
if (u_spline_type_u != -1)
|
||||
glUniform1i(u_spline_type_u, gstate_c.spline_type_u);
|
||||
render_->SetUniformI1(&u_spline_type_u, gstate_c.spline_type_u);
|
||||
if (u_spline_type_v != -1)
|
||||
glUniform1i(u_spline_type_v, gstate_c.spline_type_v);
|
||||
render_->SetUniformI1(&u_spline_type_v, gstate_c.spline_type_v);
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
ShaderManagerGLES::ShaderManagerGLES()
|
||||
: lastShader_(nullptr), shaderSwitchDirtyUniforms_(0), diskCacheDirty_(false), fsCache_(16), vsCache_(16) {
|
||||
ShaderManagerGLES::ShaderManagerGLES(GLRenderManager *render)
|
||||
: render_(render), lastShader_(nullptr), shaderSwitchDirtyUniforms_(0), diskCacheDirty_(false), fsCache_(16), vsCache_(16) {
|
||||
codeBuffer_ = new char[16384];
|
||||
lastFSID_.set_invalid();
|
||||
lastVSID_.set_invalid();
|
||||
@@ -746,8 +590,6 @@ void ShaderManagerGLES::DirtyShader() {
|
||||
}
|
||||
|
||||
void ShaderManagerGLES::DirtyLastShader() { // disables vertex arrays
|
||||
if (lastShader_)
|
||||
lastShader_->stop();
|
||||
lastShader_ = nullptr;
|
||||
lastVShaderSame_ = false;
|
||||
}
|
||||
@@ -757,7 +599,8 @@ Shader *ShaderManagerGLES::CompileFragmentShader(FShaderID FSID) {
|
||||
if (!GenerateFragmentShader(FSID, codeBuffer_, &uniformMask)) {
|
||||
return nullptr;
|
||||
}
|
||||
return new Shader(FSID, codeBuffer_, GL_FRAGMENT_SHADER, false, 0, uniformMask);
|
||||
std::string desc = FragmentShaderDesc(FSID);
|
||||
return new Shader(render_, codeBuffer_, desc, GL_FRAGMENT_SHADER, false, 0, uniformMask);
|
||||
}
|
||||
|
||||
Shader *ShaderManagerGLES::CompileVertexShader(VShaderID VSID) {
|
||||
@@ -765,7 +608,8 @@ Shader *ShaderManagerGLES::CompileVertexShader(VShaderID VSID) {
|
||||
uint32_t attrMask;
|
||||
uint64_t uniformMask;
|
||||
GenerateVertexShader(VSID, codeBuffer_, &attrMask, &uniformMask);
|
||||
return new Shader(VSID, codeBuffer_, GL_VERTEX_SHADER, useHWTransform, attrMask, uniformMask);
|
||||
std::string desc = VertexShaderDesc(VSID);
|
||||
return new Shader(render_, codeBuffer_, desc, GL_VERTEX_SHADER, useHWTransform, attrMask, uniformMask);
|
||||
}
|
||||
|
||||
Shader *ShaderManagerGLES::ApplyVertexShader(int prim, u32 vertType, VShaderID *VSID) {
|
||||
@@ -815,7 +659,7 @@ Shader *ShaderManagerGLES::ApplyVertexShader(int prim, u32 vertType, VShaderID *
|
||||
uint32_t attrMask;
|
||||
uint64_t uniformMask;
|
||||
GenerateVertexShader(vsidTemp, codeBuffer_, &attrMask, &uniformMask);
|
||||
vs = new Shader(vsidTemp, codeBuffer_, GL_VERTEX_SHADER, false, attrMask, uniformMask);
|
||||
vs = new Shader(render_, codeBuffer_, VertexShaderDesc(vsidTemp), GL_VERTEX_SHADER, false, attrMask, uniformMask);
|
||||
}
|
||||
|
||||
vsCache_.Insert(*VSID, vs);
|
||||
@@ -869,12 +713,12 @@ LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs,
|
||||
_dbg_assert_(G3D, FSID.Bit(FS_BIT_FLATSHADE) == VSID.Bit(VS_BIT_FLATSHADE));
|
||||
|
||||
// Check if we can link these.
|
||||
ls = new LinkedShader(VSID, vs, FSID, fs, vs->UseHWTransform());
|
||||
ls->use(VSID, lastShader_);
|
||||
ls = new LinkedShader(render_, VSID, vs, FSID, fs, vs->UseHWTransform());
|
||||
ls->use(VSID);
|
||||
const LinkedShaderCacheEntry entry(vs, fs, ls);
|
||||
linkedShaderCache_.push_back(entry);
|
||||
} else {
|
||||
ls->use(VSID, lastShader_);
|
||||
ls->use(VSID);
|
||||
}
|
||||
ls->UpdateUniforms(vertType, VSID);
|
||||
|
||||
@@ -953,7 +797,7 @@ std::string ShaderManagerGLES::DebugGetShaderString(std::string id, DebugShaderT
|
||||
// as sometimes these features might have an effect on the ID bits.
|
||||
|
||||
#define CACHE_HEADER_MAGIC 0x83277592
|
||||
#define CACHE_VERSION 5
|
||||
#define CACHE_VERSION 7
|
||||
struct CacheHeader {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
@@ -1033,6 +877,8 @@ bool ShaderManagerGLES::ContinuePrecompile(float sliceTime) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PSP_SetLoading("Compiling shaders...");
|
||||
|
||||
double start = real_time_now();
|
||||
// Let's try to keep it under sliceTime if possible.
|
||||
double end = start + sliceTime;
|
||||
@@ -1092,7 +938,7 @@ bool ShaderManagerGLES::ContinuePrecompile(float sliceTime) {
|
||||
Shader *vs = vsCache_.Get(vsid);
|
||||
Shader *fs = fsCache_.Get(fsid);
|
||||
if (vs && fs) {
|
||||
LinkedShader *ls = new LinkedShader(vsid, vs, fsid, fs, vs->UseHWTransform(), true);
|
||||
LinkedShader *ls = new LinkedShader(render_, vsid, vs, fsid, fs, vs->UseHWTransform(), true);
|
||||
LinkedShaderCacheEntry entry(vs, fs, ls);
|
||||
linkedShaderCache_.push_back(entry);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "Common/Hashmaps.h"
|
||||
#include "thin3d/GLRenderManager.h"
|
||||
#include "GPU/Common/ShaderCommon.h"
|
||||
#include "GPU/Common/ShaderId.h"
|
||||
#include "GPU/GLES/VertexShaderGeneratorGLES.h"
|
||||
@@ -33,28 +34,26 @@ enum {
|
||||
ATTR_POSITION = 0,
|
||||
ATTR_TEXCOORD = 1,
|
||||
ATTR_NORMAL = 2,
|
||||
ATTR_W1 = 3,
|
||||
ATTR_W2 = 4,
|
||||
ATTR_COLOR0 = 5,
|
||||
ATTR_COLOR1 = 6,
|
||||
ATTR_COLOR0 = 3,
|
||||
ATTR_COLOR1 = 4,
|
||||
|
||||
ATTR_COUNT,
|
||||
};
|
||||
|
||||
class LinkedShader {
|
||||
public:
|
||||
LinkedShader(VShaderID VSID, Shader *vs, FShaderID FSID, Shader *fs, bool useHWTransform, bool preloading = false);
|
||||
LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, FShaderID FSID, Shader *fs, bool useHWTransform, bool preloading = false);
|
||||
~LinkedShader();
|
||||
|
||||
void use(const VShaderID &VSID, LinkedShader *previous);
|
||||
void stop();
|
||||
void UpdateUniforms(u32 vertType, const VShaderID &VSID);
|
||||
void use(const ShaderID &VSID);
|
||||
void UpdateUniforms(u32 vertType, const ShaderID &VSID);
|
||||
|
||||
GLRenderManager *render_;
|
||||
Shader *vs_;
|
||||
// Set to false if the VS failed, happens on Mali-400 a lot for complex shaders.
|
||||
bool useHWTransform_;
|
||||
|
||||
uint32_t program = 0;
|
||||
GLRProgram *program;
|
||||
uint64_t availableUniforms;
|
||||
uint64_t dirtyUniforms = 0;
|
||||
|
||||
@@ -71,13 +70,6 @@ public:
|
||||
int u_world;
|
||||
int u_depthRange; // x,y = viewport xscale/xcenter. z,w=clipping minz/maxz (?)
|
||||
|
||||
#ifdef USE_BONE_ARRAY
|
||||
int u_bone; // array, size is numBones
|
||||
#else
|
||||
int u_bone[8];
|
||||
#endif
|
||||
int numBones;
|
||||
|
||||
// Shader blending.
|
||||
int u_fbotex;
|
||||
int u_blendFixA;
|
||||
@@ -108,8 +100,7 @@ public:
|
||||
int u_lightpos[4];
|
||||
int u_lightdir[4];
|
||||
int u_lightatt[4]; // attenuation
|
||||
int u_lightangle[4]; // spotlight cone angle (cosine)
|
||||
int u_lightspotCoef[4]; // spotlight dropoff
|
||||
int u_lightangle_spotCoef[4]; // spotlight cone angle (cosine) (x), spotlight dropoff (y)
|
||||
int u_lightdiffuse[4]; // each light consist of vec4[3]
|
||||
int u_lightspecular[4]; // attenuation
|
||||
int u_lightambient[4]; // attenuation
|
||||
@@ -127,12 +118,12 @@ public:
|
||||
|
||||
class Shader {
|
||||
public:
|
||||
Shader(const ShaderID &id, const char *code, uint32_t glShaderType, bool useHWTransform, uint32_t attrMask, uint64_t uniformMask);
|
||||
Shader(GLRenderManager *render, const char *code, const std::string &desc, uint32_t glShaderType, bool useHWTransform, uint32_t attrMask, uint64_t uniformMask);
|
||||
~Shader();
|
||||
uint32_t shader;
|
||||
GLRShader *shader;
|
||||
|
||||
bool Failed() const { return failed_; }
|
||||
bool UseHWTransform() const { return useHWTransform_; } // only relevant for vtx shaders
|
||||
bool UseHWTransform() const { return useHWTransform_; } // only relevant for vtx shaders
|
||||
|
||||
std::string GetShaderString(DebugShaderStringType type, ShaderID id) const;
|
||||
|
||||
@@ -140,6 +131,7 @@ public:
|
||||
uint64_t GetUniformMask() const { return uniformMask_; }
|
||||
|
||||
private:
|
||||
GLRenderManager *render_;
|
||||
std::string source_;
|
||||
bool failed_;
|
||||
bool useHWTransform_;
|
||||
@@ -150,7 +142,7 @@ private:
|
||||
|
||||
class ShaderManagerGLES : public ShaderManagerCommon {
|
||||
public:
|
||||
ShaderManagerGLES();
|
||||
ShaderManagerGLES(GLRenderManager *render);
|
||||
~ShaderManagerGLES();
|
||||
|
||||
void ClearCache(bool deleteThem); // TODO: deleteThem currently not respected
|
||||
@@ -189,6 +181,7 @@ private:
|
||||
};
|
||||
typedef std::vector<LinkedShaderCacheEntry> LinkedShaderCache;
|
||||
|
||||
GLRenderManager *render_;
|
||||
LinkedShaderCache linkedShaderCache_;
|
||||
|
||||
bool lastVShaderSame_;
|
||||
|
||||
+58
-123
@@ -23,6 +23,7 @@
|
||||
#include "StateMappingGLES.h"
|
||||
#include "profiler/profiler.h"
|
||||
#include "gfx/gl_debug_log.h"
|
||||
#include "thin3d/GLRenderManager.h"
|
||||
|
||||
#include "GPU/Math3D.h"
|
||||
#include "GPU/GPUState.h"
|
||||
@@ -31,7 +32,6 @@
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "GPU/GLES/GPU_GLES.h"
|
||||
#include "ext/native/gfx/GLStateCache.h"
|
||||
#include "GPU/GLES/ShaderManagerGLES.h"
|
||||
#include "GPU/GLES/TextureCacheGLES.h"
|
||||
#include "GPU/GLES/FramebufferManagerGLES.h"
|
||||
@@ -123,18 +123,14 @@ static const GLushort logicOps[] = {
|
||||
|
||||
inline void DrawEngineGLES::ResetShaderBlending() {
|
||||
if (fboTexBound_) {
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
renderManager->BindTexture(1, nullptr);
|
||||
fboTexBound_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawEngineGLES::ApplyDrawState(int prim) {
|
||||
if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) {
|
||||
textureCache_->SetTexture();
|
||||
gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);
|
||||
}
|
||||
GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
|
||||
if (!gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE)) {
|
||||
// Nothing to do, let's early-out
|
||||
@@ -163,17 +159,10 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
|
||||
gstate_c.SetAllowShaderBlend(!g_Config.bDisableSlowFramebufEffects);
|
||||
|
||||
if (gstate.isModeClear()) {
|
||||
glstate.blend.disable();
|
||||
// Color Test
|
||||
bool colorMask = gstate.isClearModeColorMask();
|
||||
bool alphaMask = gstate.isClearModeAlphaMask();
|
||||
glstate.colorMask.set(colorMask, colorMask, colorMask, alphaMask);
|
||||
#ifndef USING_GLES2
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) {
|
||||
// Logic Ops
|
||||
glstate.colorLogicOp.disable();
|
||||
}
|
||||
#endif
|
||||
renderManager->SetNoBlendAndMask((colorMask ? 7 : 0) | (alphaMask ? 8 : 0));
|
||||
} else {
|
||||
// Do the large chunks of state conversion. We might be able to hide these two behind a dirty-flag each,
|
||||
// to avoid recomputing heavy stuff unnecessarily every draw call.
|
||||
@@ -184,6 +173,21 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
|
||||
if (ApplyShaderBlending()) {
|
||||
// We may still want to do something about stencil -> alpha.
|
||||
ApplyStencilReplaceAndLogicOp(blendState.replaceAlphaWithStencil, blendState);
|
||||
|
||||
// We copy the framebuffer here, as doing so will wipe any blend state if we do it later.
|
||||
if (fboTexNeedBind_) {
|
||||
// Note that this is positions, not UVs, that we need the copy from.
|
||||
// TODO: If the device doesn't support blit, this will corrupt the currently applied texture.
|
||||
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY);
|
||||
// If we are rendering at a higher resolution, linear is probably best for the dest color.
|
||||
renderManager->SetTextureSampler(1, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_LINEAR, GL_LINEAR, 0.0f);
|
||||
fboTexBound_ = true;
|
||||
fboTexNeedBind_ = false;
|
||||
|
||||
framebufferManager_->RebindFramebuffer();
|
||||
// Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects.
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE);
|
||||
}
|
||||
} else {
|
||||
// Until next time, force it off.
|
||||
ResetShaderBlending();
|
||||
@@ -194,11 +198,6 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
|
||||
}
|
||||
|
||||
if (blendState.enabled) {
|
||||
glstate.blend.enable();
|
||||
glstate.blendEquationSeparate.set(glBlendEqLookup[(size_t)blendState.eqColor], glBlendEqLookup[(size_t)blendState.eqAlpha]);
|
||||
glstate.blendFuncSeparate.set(
|
||||
glBlendFactorLookup[(size_t)blendState.srcColor], glBlendFactorLookup[(size_t)blendState.dstColor],
|
||||
glBlendFactorLookup[(size_t)blendState.srcAlpha], glBlendFactorLookup[(size_t)blendState.dstAlpha]);
|
||||
if (blendState.dirtyShaderBlend) {
|
||||
gstate_c.Dirty(DIRTY_SHADERBLEND);
|
||||
}
|
||||
@@ -210,10 +209,8 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
|
||||
(float)((color & 0xFF0000) >> 16) * (1.0f / 255.0f),
|
||||
(float)((color & 0xFF000000) >> 24) * (1.0f / 255.0f),
|
||||
};
|
||||
glstate.blendColor.set(col);
|
||||
renderManager->SetBlendFactor(col);
|
||||
}
|
||||
} else {
|
||||
glstate.blend.disable();
|
||||
}
|
||||
|
||||
// PSP color/alpha mask is per bit but we can only support per byte.
|
||||
@@ -235,19 +232,20 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
|
||||
WARN_LOG_REPORT_ONCE(amask, G3D, "Unsupported alpha/stencil mask: %02x", abits);
|
||||
}
|
||||
#endif
|
||||
|
||||
glstate.colorMask.set(rmask, gmask, bmask, amask);
|
||||
int mask = (int)rmask | ((int)gmask << 1) | ((int)bmask << 2) | ((int)amask << 3);
|
||||
if (blendState.enabled) {
|
||||
renderManager->SetBlendAndMask(mask, blendState.enabled,
|
||||
glBlendFactorLookup[(size_t)blendState.srcColor], glBlendFactorLookup[(size_t)blendState.dstColor],
|
||||
glBlendFactorLookup[(size_t)blendState.srcAlpha], glBlendFactorLookup[(size_t)blendState.dstAlpha],
|
||||
glBlendEqLookup[(size_t)blendState.eqColor], glBlendEqLookup[(size_t)blendState.eqAlpha]);
|
||||
} else {
|
||||
renderManager->SetNoBlendAndMask(mask);
|
||||
}
|
||||
|
||||
#ifndef USING_GLES2
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) {
|
||||
// TODO: Make this dynamic
|
||||
// Logic Ops
|
||||
if (gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY) {
|
||||
glstate.colorLogicOp.enable();
|
||||
glstate.logicOp.set(logicOps[gstate.getLogicOp()]);
|
||||
} else {
|
||||
glstate.colorLogicOp.disable();
|
||||
}
|
||||
renderManager->SetLogicOp(gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY,
|
||||
logicOps[gstate.getLogicOp()]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -257,26 +255,18 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
|
||||
gstate_c.Clean(DIRTY_RASTER_STATE);
|
||||
|
||||
// Dither
|
||||
if (gstate.isDitherEnabled()) {
|
||||
glstate.dither.enable();
|
||||
glstate.dither.set(true);
|
||||
} else {
|
||||
glstate.dither.disable();
|
||||
}
|
||||
bool dither = gstate.isDitherEnabled();
|
||||
bool cullEnable;
|
||||
GLenum cullMode = cullingMode[gstate.getCullMode() ^ !useBufferedRendering];
|
||||
|
||||
if (gstate.isModeClear()) {
|
||||
// Culling
|
||||
glstate.cullFace.disable();
|
||||
cullEnable = false;
|
||||
} else {
|
||||
// Set cull
|
||||
bool cullEnabled = !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES && gstate.isCullEnabled();
|
||||
if (cullEnabled) {
|
||||
glstate.cullFace.enable();
|
||||
glstate.cullFaceMode.set(cullingMode[gstate.getCullMode() ^ !useBufferedRendering]);
|
||||
} else {
|
||||
glstate.cullFace.disable();
|
||||
}
|
||||
cullEnable = !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES && gstate.isCullEnabled();
|
||||
}
|
||||
renderManager->SetRaster(cullEnable, GL_CCW, cullMode, dither);
|
||||
}
|
||||
|
||||
if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) {
|
||||
@@ -284,77 +274,43 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
|
||||
bool enableStencilTest = !g_Config.bDisableStencilTest;
|
||||
if (gstate.isModeClear()) {
|
||||
// Depth Test
|
||||
glstate.depthTest.enable();
|
||||
glstate.depthFunc.set(GL_ALWAYS);
|
||||
glstate.depthWrite.set(gstate.isClearModeDepthMask() ? GL_TRUE : GL_FALSE);
|
||||
if (gstate.isClearModeDepthMask()) {
|
||||
framebufferManager_->SetDepthUpdated();
|
||||
}
|
||||
|
||||
// Stencil Test
|
||||
if (gstate.isClearModeAlphaMask() && enableStencilTest) {
|
||||
glstate.stencilTest.enable();
|
||||
glstate.stencilOp.set(GL_REPLACE, GL_REPLACE, GL_REPLACE);
|
||||
// TODO: In clear mode, the stencil value is set to the alpha value of the vertex.
|
||||
// A normal clear will be 2 points, the second point has the color.
|
||||
// We should set "ref" to that value instead of 0.
|
||||
// In case of clear rectangles, we set it again once we know what the color is.
|
||||
glstate.stencilFunc.set(GL_ALWAYS, 255, 0xFF);
|
||||
glstate.stencilMask.set(0xFF);
|
||||
} else {
|
||||
glstate.stencilTest.disable();
|
||||
}
|
||||
renderManager->SetStencilFunc(gstate.isClearModeAlphaMask() && enableStencilTest, GL_ALWAYS, 0xFF, 0xFF);
|
||||
renderManager->SetStencilOp(0xFF, GL_REPLACE, GL_REPLACE, GL_REPLACE);
|
||||
renderManager->SetDepth(true, gstate.isClearModeDepthMask() ? true : false, GL_ALWAYS);
|
||||
} else {
|
||||
// Depth Test
|
||||
if (gstate.isDepthTestEnabled()) {
|
||||
glstate.depthTest.enable();
|
||||
glstate.depthFunc.set(compareOps[gstate.getDepthTestFunction()]);
|
||||
glstate.depthWrite.set(gstate.isDepthWriteEnabled() ? GL_TRUE : GL_FALSE);
|
||||
if (gstate.isDepthWriteEnabled()) {
|
||||
framebufferManager_->SetDepthUpdated();
|
||||
}
|
||||
} else {
|
||||
glstate.depthTest.disable();
|
||||
renderManager->SetDepth(gstate.isDepthTestEnabled(), gstate.isDepthWriteEnabled(), compareOps[gstate.getDepthTestFunction()]);
|
||||
if (gstate.isDepthTestEnabled() && gstate.isDepthWriteEnabled()) {
|
||||
framebufferManager_->SetDepthUpdated();
|
||||
}
|
||||
|
||||
GenericStencilFuncState stencilState;
|
||||
ConvertStencilFuncState(stencilState);
|
||||
|
||||
// Stencil Test
|
||||
if (stencilState.enabled) {
|
||||
glstate.stencilTest.enable();
|
||||
glstate.stencilFunc.set(compareOps[stencilState.testFunc], stencilState.testRef, stencilState.testMask);
|
||||
glstate.stencilOp.set(stencilOps[stencilState.sFail], stencilOps[stencilState.zFail], stencilOps[stencilState.zPass]);
|
||||
glstate.stencilMask.set(stencilState.writeMask);
|
||||
renderManager->SetStencilFunc(stencilState.enabled, compareOps[stencilState.testFunc], stencilState.testRef, stencilState.testMask);
|
||||
renderManager->SetStencilOp(stencilState.writeMask, stencilOps[stencilState.sFail], stencilOps[stencilState.zFail], stencilOps[stencilState.zPass]);
|
||||
} else {
|
||||
glstate.stencilTest.disable();
|
||||
renderManager->SetStencilDisabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) {
|
||||
gstate_c.Clean(DIRTY_VIEWPORTSCISSOR_STATE);
|
||||
ViewportAndScissor vpAndScissor;
|
||||
ConvertViewportAndScissor(useBufferedRendering,
|
||||
framebufferManager_->GetRenderWidth(), framebufferManager_->GetRenderHeight(),
|
||||
framebufferManager_->GetTargetBufferWidth(), framebufferManager_->GetTargetBufferHeight(),
|
||||
vpAndScissor);
|
||||
|
||||
if (vpAndScissor.scissorEnable) {
|
||||
glstate.scissorTest.enable();
|
||||
if (!useBufferedRendering) {
|
||||
vpAndScissor.scissorY = PSP_CoreParameter().pixelHeight - vpAndScissor.scissorH - vpAndScissor.scissorY;
|
||||
}
|
||||
glstate.scissorRect.set(vpAndScissor.scissorX, vpAndScissor.scissorY, vpAndScissor.scissorW, vpAndScissor.scissorH);
|
||||
} else {
|
||||
glstate.scissorTest.disable();
|
||||
}
|
||||
|
||||
if (!useBufferedRendering) {
|
||||
vpAndScissor.viewportY = PSP_CoreParameter().pixelHeight - vpAndScissor.viewportH - vpAndScissor.viewportY;
|
||||
}
|
||||
glstate.viewport.set(vpAndScissor.viewportX, vpAndScissor.viewportY, vpAndScissor.viewportW, vpAndScissor.viewportH);
|
||||
glstate.depthRange.set(vpAndScissor.depthRangeMin, vpAndScissor.depthRangeMax);
|
||||
renderManager->SetScissor(GLRect2D{ vpAndScissor.scissorX, vpAndScissor.scissorY, vpAndScissor.scissorW, vpAndScissor.scissorH });
|
||||
renderManager->SetViewport({
|
||||
vpAndScissor.viewportX, vpAndScissor.viewportY,
|
||||
vpAndScissor.viewportW, vpAndScissor.viewportH,
|
||||
vpAndScissor.depthRangeMin, vpAndScissor.depthRangeMax });
|
||||
|
||||
if (vpAndScissor.dirtyProj) {
|
||||
gstate_c.Dirty(DIRTY_PROJMATRIX);
|
||||
@@ -363,40 +319,19 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
|
||||
gstate_c.Dirty(DIRTY_DEPTHRANGE);
|
||||
}
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
void DrawEngineGLES::ApplyDrawStateLate() {
|
||||
void DrawEngineGLES::ApplyDrawStateLate(bool setStencil, int stencilValue) {
|
||||
if (setStencil) {
|
||||
render_->SetStencilFunc(GL_TRUE, GL_ALWAYS, stencilValue, 255);
|
||||
}
|
||||
|
||||
// At this point, we know if the vertices are full alpha or not.
|
||||
// TODO: Set the nearest/linear here (since we correctly know if alpha/color tests are needed)?
|
||||
if (!gstate.isModeClear()) {
|
||||
if (fboTexNeedBind_) {
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
// Note that this is positions, not UVs, that we need the copy from.
|
||||
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY);
|
||||
framebufferManager_->RebindFramebuffer();
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
// If we are rendering at a higher resolution, linear is probably best for the dest color.
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
fboTexBound_ = true;
|
||||
fboTexNeedBind_ = false;
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
|
||||
// Apply the texture after the FBO tex, since it might unbind the texture.
|
||||
// TODO: Could use a separate texture unit to be safer?
|
||||
textureCache_->ApplyTexture();
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
|
||||
// Apply last, once we know the alpha params of the texture.
|
||||
if (gstate.isAlphaTestEnabled() || gstate.isColorTestEnabled()) {
|
||||
fragmentTestCache_->BindTestTexture(GL_TEXTURE2);
|
||||
fragmentTestCache_->BindTestTexture(2);
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
#include "gfx_es2/glsl_program.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "ext/native/gfx/GLStateCache.h"
|
||||
#include "GPU/Common/StencilCommon.h"
|
||||
#include "GPU/GLES/FramebufferManagerGLES.h"
|
||||
#include "GPU/GLES/ShaderManagerGLES.h"
|
||||
@@ -111,12 +110,10 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe
|
||||
}
|
||||
|
||||
// Let's not bother with the shader if it's just zero.
|
||||
glstate.scissorTest.disable();
|
||||
glstate.colorMask.set(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
|
||||
glClearColor(0, 0, 0, 0);
|
||||
glClearStencil(0);
|
||||
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (dstBuffer->fbo) {
|
||||
draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR });
|
||||
}
|
||||
render_->Clear(0, 0, 0, GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT, 0x8, 0, 0, 0, 0);
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE);
|
||||
return true;
|
||||
}
|
||||
@@ -126,27 +123,25 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe
|
||||
static std::string vs_code, fs_code;
|
||||
vs_code = ApplyGLSLPrelude(stencil_vs, GL_VERTEX_SHADER);
|
||||
fs_code = ApplyGLSLPrelude(stencil_fs, GL_FRAGMENT_SHADER);
|
||||
stencilUploadProgram_ = glsl_create_source(vs_code.c_str(), fs_code.c_str(), &errorString);
|
||||
std::vector<GLRShader *> shaders;
|
||||
shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, vs_code, "stencil"));
|
||||
shaders.push_back(render_->CreateShader(GL_FRAGMENT_SHADER, fs_code, "stencil"));
|
||||
std::vector<GLRProgram::UniformLocQuery> queries;
|
||||
queries.push_back({ &u_stencilUploadTex, "tex" });
|
||||
queries.push_back({ &u_stencilValue, "u_stencilValue" });
|
||||
std::vector<GLRProgram::Initializer> inits;
|
||||
inits.push_back({ &u_stencilUploadTex, 0, 0 });
|
||||
stencilUploadProgram_ = render_->CreateProgram(shaders, {}, queries, inits, false);
|
||||
for (auto iter : shaders) {
|
||||
render_->DeleteShader(iter);
|
||||
}
|
||||
if (!stencilUploadProgram_) {
|
||||
ERROR_LOG_REPORT(G3D, "Failed to compile stencilUploadProgram! This shouldn't happen.\n%s", errorString.c_str());
|
||||
} else {
|
||||
glsl_bind(stencilUploadProgram_);
|
||||
}
|
||||
|
||||
GLint u_tex = glsl_uniform_loc(stencilUploadProgram_, "tex");
|
||||
glUniform1i(u_tex, 0);
|
||||
} else {
|
||||
glsl_bind(stencilUploadProgram_);
|
||||
}
|
||||
|
||||
shaderManagerGL_->DirtyLastShader();
|
||||
|
||||
DisableState();
|
||||
glstate.colorMask.set(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
|
||||
glstate.stencilTest.enable();
|
||||
glstate.stencilOp.set(GL_REPLACE, GL_REPLACE, GL_REPLACE);
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE);
|
||||
|
||||
bool useBlit = gstate_c.Supports(GPU_SUPPORTS_ARB_FRAMEBUFFER_BLIT | GPU_SUPPORTS_NV_FRAMEBUFFER_BLIT);
|
||||
|
||||
// Our fragment shader (and discard) is slow. Since the source is 1x, we can stencil to 1x.
|
||||
@@ -164,43 +159,44 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe
|
||||
} else if (dstBuffer->fbo) {
|
||||
draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR });
|
||||
}
|
||||
glViewport(0, 0, w, h);
|
||||
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE);
|
||||
render_->SetViewport({ 0, 0, (float)w, (float)h, 0.0f, 1.0f });
|
||||
|
||||
float u1 = 1.0f;
|
||||
float v1 = 1.0f;
|
||||
MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1);
|
||||
textureCacheGL_->ForgetLastTexture();
|
||||
|
||||
glClearStencil(0);
|
||||
glClear(GL_STENCIL_BUFFER_BIT);
|
||||
// We must bind the program after starting the render pass, and set the color mask after clearing.
|
||||
render_->SetDepth(false, false, GL_ALWAYS);
|
||||
render_->Clear(0, 0, 0, GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, 0x8, 0, 0, 0, 0);
|
||||
render_->SetStencilFunc(GL_TRUE, GL_ALWAYS, 0xFF, 0xFF);
|
||||
render_->SetRaster(false, GL_CCW, GL_FRONT, GL_FALSE);
|
||||
render_->BindProgram(stencilUploadProgram_);
|
||||
render_->SetNoBlendAndMask(0x8);
|
||||
|
||||
glstate.stencilFunc.set(GL_ALWAYS, 0xFF, 0xFF);
|
||||
|
||||
GLint u_stencilValue = glsl_uniform_loc(stencilUploadProgram_, "u_stencilValue");
|
||||
for (int i = 1; i < values; i += i) {
|
||||
if (!(usedBits & i)) {
|
||||
// It's already zero, let's skip it.
|
||||
continue;
|
||||
}
|
||||
if (dstBuffer->format == GE_FORMAT_4444) {
|
||||
glstate.stencilMask.set((i << 4) | i);
|
||||
glUniform1f(u_stencilValue, i * (16.0f / 255.0f));
|
||||
render_->SetStencilOp((i << 4) | i, GL_REPLACE, GL_REPLACE, GL_REPLACE);
|
||||
render_->SetUniformF1(&u_stencilValue, i * (16.0f / 255.0f));
|
||||
} else if (dstBuffer->format == GE_FORMAT_5551) {
|
||||
glstate.stencilMask.set(0xFF);
|
||||
glUniform1f(u_stencilValue, i * (128.0f / 255.0f));
|
||||
render_->SetStencilOp(0xFF, GL_REPLACE, GL_REPLACE, GL_REPLACE);
|
||||
render_->SetUniformF1(&u_stencilValue, i * (128.0f / 255.0f));
|
||||
} else {
|
||||
glstate.stencilMask.set(i);
|
||||
glUniform1f(u_stencilValue, i * (1.0f / 255.0f));
|
||||
render_->SetStencilOp(i, GL_REPLACE, GL_REPLACE, GL_REPLACE);
|
||||
render_->SetUniformF1(&u_stencilValue, i * (1.0f / 255.0f));
|
||||
}
|
||||
DrawActiveTexture(0, 0, dstBuffer->width, dstBuffer->height, dstBuffer->bufferWidth, dstBuffer->bufferHeight, 0.0f, 0.0f, u1, v1, ROTATION_LOCKED_HORIZONTAL, DRAWTEX_NEAREST);
|
||||
DrawActiveTexture(0, 0, dstBuffer->width, dstBuffer->height, dstBuffer->bufferWidth, dstBuffer->bufferHeight, 0.0f, 0.0f, u1, v1, ROTATION_LOCKED_HORIZONTAL, DRAWTEX_NEAREST | DRAWTEX_KEEP_STENCIL_ALPHA);
|
||||
}
|
||||
glstate.stencilMask.set(0xFF);
|
||||
|
||||
if (useBlit) {
|
||||
draw_->BlitFramebuffer(blitFBO, 0, 0, w, h, dstBuffer->fbo, 0, 0, dstBuffer->renderWidth, dstBuffer->renderHeight, Draw::FB_STENCIL_BIT, Draw::FB_BLIT_NEAREST);
|
||||
}
|
||||
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE);
|
||||
RebindFramebuffer();
|
||||
return true;
|
||||
}
|
||||
+151
-360
@@ -23,6 +23,7 @@
|
||||
#include "i18n/i18n.h"
|
||||
#include "math/math_util.h"
|
||||
#include "profiler/profiler.h"
|
||||
#include "thin3d/GLRenderManager.h"
|
||||
|
||||
#include "Common/ColorConv.h"
|
||||
#include "Core/Config.h"
|
||||
@@ -31,7 +32,6 @@
|
||||
#include "Core/Reporting.h"
|
||||
#include "GPU/ge_constants.h"
|
||||
#include "GPU/GPUState.h"
|
||||
#include "ext/native/gfx/GLStateCache.h"
|
||||
#include "GPU/GLES/TextureCacheGLES.h"
|
||||
#include "GPU/GLES/FramebufferManagerGLES.h"
|
||||
#include "GPU/GLES/FragmentShaderGeneratorGLES.h"
|
||||
@@ -44,24 +44,23 @@
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
|
||||
#ifndef GL_UNPACK_ROW_LENGTH
|
||||
#define GL_UNPACK_ROW_LENGTH 0x0CF2
|
||||
#endif
|
||||
|
||||
#define TEXCACHE_NAME_CACHE_SIZE 16
|
||||
|
||||
TextureCacheGLES::TextureCacheGLES(Draw::DrawContext *draw)
|
||||
: TextureCacheCommon(draw) {
|
||||
timesInvalidatedAllThisFrame_ = 0;
|
||||
lastBoundTexture = INVALID_TEX;
|
||||
lastBoundTexture = nullptr;
|
||||
render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
|
||||
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropyLevel);
|
||||
SetupTextureDecoder();
|
||||
|
||||
nextTexture_ = nullptr;
|
||||
std::vector<GLRInputLayout::Entry> entries;
|
||||
entries.push_back({ 0, 3, GL_FLOAT, GL_FALSE, 20, 0 });
|
||||
entries.push_back({ 1, 2, GL_FLOAT, GL_FALSE, 20, 12 });
|
||||
shadeInputLayout_ = render_->CreateInputLayout(entries);
|
||||
}
|
||||
|
||||
TextureCacheGLES::~TextureCacheGLES() {
|
||||
render_->DeleteInputLayout(shadeInputLayout_);
|
||||
Clear(true);
|
||||
}
|
||||
|
||||
@@ -71,23 +70,17 @@ void TextureCacheGLES::SetFramebufferManager(FramebufferManagerGLES *fbManager)
|
||||
}
|
||||
|
||||
void TextureCacheGLES::ReleaseTexture(TexCacheEntry *entry, bool delete_them) {
|
||||
DEBUG_LOG(G3D, "Deleting texture %i", entry->textureName);
|
||||
DEBUG_LOG(G3D, "Deleting texture %08x", entry->addr);
|
||||
if (delete_them) {
|
||||
if (entry->textureName != 0) {
|
||||
glDeleteTextures(1, &entry->textureName);
|
||||
if (entry->textureName) {
|
||||
render_->DeleteTexture(entry->textureName);
|
||||
}
|
||||
}
|
||||
entry->textureName = 0;
|
||||
entry->textureName = nullptr;
|
||||
}
|
||||
|
||||
void TextureCacheGLES::Clear(bool delete_them) {
|
||||
TextureCacheCommon::Clear(delete_them);
|
||||
if (delete_them) {
|
||||
if (!nameCache_.empty()) {
|
||||
glDeleteTextures((GLsizei)nameCache_.size(), &nameCache_[0]);
|
||||
nameCache_.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GLenum getClutDestFormat(GEPaletteFormat format) {
|
||||
@@ -122,7 +115,6 @@ static const GLuint MagFiltGL[2] = {
|
||||
|
||||
// This should not have to be done per texture! OpenGL is silly yo
|
||||
void TextureCacheGLES::UpdateSamplingParams(TexCacheEntry &entry, bool force) {
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
int minFilt;
|
||||
int magFilt;
|
||||
bool sClamp;
|
||||
@@ -133,59 +125,37 @@ void TextureCacheGLES::UpdateSamplingParams(TexCacheEntry &entry, bool force) {
|
||||
GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, maxLevel, entry.addr, mode);
|
||||
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_TEXTURE_LOD_CONTROL)) {
|
||||
float minLod = 0.0f;
|
||||
float maxLod = 0.0f;
|
||||
if (maxLevel != 0) {
|
||||
// TODO: What about a swap of autoMip mode?
|
||||
if (force || entry.lodBias != lodBias) {
|
||||
if (true) {
|
||||
if (mode == GE_TEXLEVEL_MODE_AUTO) {
|
||||
#ifndef USING_GLES2
|
||||
// Sigh, LOD_BIAS is not even in ES 3.0.. but we could do it in the shader via texture()...
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, lodBias);
|
||||
#endif
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, (float)maxLevel);
|
||||
minLod = 0.0f;
|
||||
maxLod = (float)maxLevel;
|
||||
} else if (mode == GE_TEXLEVEL_MODE_CONST) {
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, std::max(0.0f, std::min((float)maxLevel, lodBias)));
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, std::max(0.0f, std::min((float)maxLevel, lodBias)));
|
||||
minLod = std::max(0.0f, std::min((float)maxLevel, lodBias));
|
||||
maxLod = std::max(0.0f, std::min((float)maxLevel, lodBias));
|
||||
} else { // mode == GE_TEXLEVEL_MODE_SLOPE) {
|
||||
// It's incorrect to use the slope as a bias. Instead it should be passed
|
||||
// into the shader directly as an explicit lod level, with the bias on top. For now, we just kill the
|
||||
// lodBias in this mode, working around #9772.
|
||||
#ifndef USING_GLES2
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f);
|
||||
lodBias = 0.0f;
|
||||
#endif
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, (float)maxLevel);
|
||||
minLod = 0.0f;
|
||||
maxLod = (float)maxLevel;
|
||||
}
|
||||
entry.lodBias = lodBias;
|
||||
}
|
||||
} else {
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0.0f);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 0.0f);
|
||||
minLod = 0.0f;
|
||||
maxLod = 0.0f;
|
||||
}
|
||||
render_->SetTextureLod(0, minLod, maxLod, lodBias);
|
||||
}
|
||||
|
||||
if (force || entry.minFilt != minFilt) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, MinFiltGL[minFilt]);
|
||||
entry.minFilt = minFilt;
|
||||
}
|
||||
if (force || entry.magFilt != magFilt) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, MagFiltGL[magFilt]);
|
||||
entry.magFilt = magFilt;
|
||||
}
|
||||
|
||||
if (entry.framebuffer) {
|
||||
WARN_LOG_REPORT_ONCE(wrongFramebufAttach, G3D, "Framebuffer still attached in UpdateSamplingParams()?");
|
||||
}
|
||||
|
||||
if (force || entry.sClamp != sClamp) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT);
|
||||
entry.sClamp = sClamp;
|
||||
}
|
||||
if (force || entry.tClamp != tClamp) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT);
|
||||
entry.tClamp = tClamp;
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
float aniso = 0.0f;
|
||||
render_->SetTextureSampler(0, sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT, tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT, MagFiltGL[magFilt], MinFiltGL[minFilt], aniso);
|
||||
}
|
||||
|
||||
void TextureCacheGLES::SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight) {
|
||||
@@ -199,8 +169,8 @@ void TextureCacheGLES::SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferH
|
||||
|
||||
minFilt &= 1; // framebuffers can't mipmap.
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, MinFiltGL[minFilt]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, MagFiltGL[magFilt]);
|
||||
float aniso = 0.0f;
|
||||
render_->SetTextureSampler(0, sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT, tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT, MagFiltGL[magFilt], MinFiltGL[minFilt], aniso);
|
||||
|
||||
// Often the framebuffer will not match the texture size. We'll wrap/clamp in the shader in that case.
|
||||
// This happens whether we have OES_texture_npot or not.
|
||||
@@ -209,9 +179,6 @@ void TextureCacheGLES::SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferH
|
||||
if (w != bufferWidth || h != bufferHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT);
|
||||
}
|
||||
|
||||
static void ConvertColors(void *dstBuf, const void *srcBuf, GLuint dstFmt, int numPixels) {
|
||||
@@ -240,6 +207,19 @@ void TextureCacheGLES::StartFrame() {
|
||||
InvalidateLastTexture();
|
||||
timesInvalidatedAllThisFrame_ = 0;
|
||||
|
||||
GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
if (!lowMemoryMode_ && renderManager->SawOutOfMemory()) {
|
||||
lowMemoryMode_ = true;
|
||||
decimationCounter_ = 0;
|
||||
|
||||
I18NCategory *err = GetI18NCategory("Error");
|
||||
if (standardScaleFactor_ > 1) {
|
||||
host->NotifyUserMessage(err->T("Warning: Video memory FULL, reducing upscaling and switching to slow caching mode"), 2.0f);
|
||||
} else {
|
||||
host->NotifyUserMessage(err->T("Warning: Video memory FULL, switching to slow caching mode"), 2.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (texelsScaledThisFrame_) {
|
||||
// INFO_LOG(G3D, "Scaled %i texels", texelsScaledThisFrame_);
|
||||
}
|
||||
@@ -336,39 +316,26 @@ bool SetDebugTexture() {
|
||||
#endif
|
||||
|
||||
void TextureCacheGLES::BindTexture(TexCacheEntry *entry) {
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
if (entry->textureName != lastBoundTexture) {
|
||||
glBindTexture(GL_TEXTURE_2D, entry->textureName);
|
||||
render_->BindTexture(0, entry->textureName);
|
||||
lastBoundTexture = entry->textureName;
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
UpdateSamplingParams(*entry, false);
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
void TextureCacheGLES::Unbind() {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
render_->BindTexture(0, nullptr);
|
||||
InvalidateLastTexture();
|
||||
}
|
||||
|
||||
class TextureShaderApplier {
|
||||
public:
|
||||
struct Pos {
|
||||
Pos(float x_, float y_, float z_) : x(x_), y(y_), z(z_) {
|
||||
}
|
||||
Pos() {
|
||||
}
|
||||
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
struct UV {
|
||||
UV(float u_, float v_) : u(u_), v(v_) {
|
||||
}
|
||||
UV() {
|
||||
}
|
||||
|
||||
float u;
|
||||
float v;
|
||||
};
|
||||
@@ -411,66 +378,44 @@ public:
|
||||
const float top = v1 * invHalfHeight - 1.0f;
|
||||
const float bottom = v2 * invHalfHeight - 1.0f;
|
||||
// Points are: BL, BR, TR, TL.
|
||||
pos_[0] = Pos(left, bottom, -1.0f);
|
||||
pos_[1] = Pos(right, bottom, -1.0f);
|
||||
pos_[2] = Pos(right, top, -1.0f);
|
||||
pos_[3] = Pos(left, top, -1.0f);
|
||||
pos_[0] = Pos{ left, bottom, -1.0f };
|
||||
pos_[1] = Pos{ right, bottom, -1.0f };
|
||||
pos_[2] = Pos{ right, top, -1.0f };
|
||||
pos_[3] = Pos{ left, top, -1.0f };
|
||||
|
||||
// And also the UVs, same order.
|
||||
const float uvleft = u1 * invWidth;
|
||||
const float uvright = u2 * invWidth;
|
||||
const float uvtop = v1 * invHeight;
|
||||
const float uvbottom = v2 * invHeight;
|
||||
uv_[0] = UV(uvleft, uvbottom);
|
||||
uv_[1] = UV(uvright, uvbottom);
|
||||
uv_[2] = UV(uvright, uvtop);
|
||||
uv_[3] = UV(uvleft, uvtop);
|
||||
uv_[0] = UV{ uvleft, uvbottom };
|
||||
uv_[1] = UV{ uvright, uvbottom };
|
||||
uv_[2] = UV{ uvright, uvtop };
|
||||
uv_[3] = UV{ uvleft, uvtop };
|
||||
}
|
||||
}
|
||||
|
||||
void Use(DrawEngineGLES *transformDraw) {
|
||||
glUseProgram(shader_->program);
|
||||
|
||||
// Restore will rebind all of the state below.
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_VAO)) {
|
||||
static const GLubyte indices[4] = { 0, 1, 3, 2 };
|
||||
transformDraw->BindBuffer(pos_, sizeof(pos_), uv_, sizeof(uv_));
|
||||
transformDraw->BindElementBuffer(indices, sizeof(indices));
|
||||
} else {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
void Use(GLRenderManager *render, DrawEngineGLES *transformDraw, GLRInputLayout *inputLayout) {
|
||||
render->BindProgram(shader_->program);
|
||||
struct SimpleVertex {
|
||||
float pos[3];
|
||||
float uv[2];
|
||||
};
|
||||
uint32_t bindOffset;
|
||||
GLRBuffer *bindBuffer;
|
||||
SimpleVertex *verts = (SimpleVertex *)transformDraw->GetPushVertexBuffer()->Push(sizeof(SimpleVertex) * 4, &bindOffset, &bindBuffer);
|
||||
int order[4] = { 0 ,1, 3, 2 };
|
||||
for (int i = 0; i < 4; i++) {
|
||||
memcpy(verts[i].pos, &pos_[order[i]], sizeof(Pos));
|
||||
memcpy(verts[i].uv, &uv_[order[i]], sizeof(UV));
|
||||
}
|
||||
glEnableVertexAttribArray(shader_->a_position);
|
||||
glEnableVertexAttribArray(shader_->a_texcoord0);
|
||||
render->BindVertexBuffer(inputLayout, bindBuffer, bindOffset);
|
||||
}
|
||||
|
||||
void Shade() {
|
||||
void Shade(GLRenderManager *render) {
|
||||
static const GLubyte indices[4] = { 0, 1, 3, 2 };
|
||||
|
||||
glstate.blend.force(false);
|
||||
glstate.colorMask.force(true, true, true, true);
|
||||
glstate.scissorTest.force(false);
|
||||
glstate.cullFace.force(false);
|
||||
glstate.depthTest.force(false);
|
||||
glstate.stencilTest.force(false);
|
||||
#if !defined(USING_GLES2)
|
||||
glstate.colorLogicOp.force(false);
|
||||
#endif
|
||||
glViewport(0, 0, renderW_, renderH_);
|
||||
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_VAO)) {
|
||||
glVertexAttribPointer(shader_->a_position, 3, GL_FLOAT, GL_FALSE, 12, 0);
|
||||
glVertexAttribPointer(shader_->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, (void *)sizeof(pos_));
|
||||
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, 0);
|
||||
} else {
|
||||
glVertexAttribPointer(shader_->a_position, 3, GL_FLOAT, GL_FALSE, 12, pos_);
|
||||
glVertexAttribPointer(shader_->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, uv_);
|
||||
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices);
|
||||
}
|
||||
glDisableVertexAttribArray(shader_->a_position);
|
||||
glDisableVertexAttribArray(shader_->a_texcoord0);
|
||||
|
||||
glstate.Restore();
|
||||
render->SetViewport(GLRViewport{ 0, 0, (float)renderW_, (float)renderH_, 0.0f, 1.0f });
|
||||
render->Draw(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -491,31 +436,29 @@ void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFram
|
||||
}
|
||||
if (depal) {
|
||||
const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat();
|
||||
GLuint clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_);
|
||||
GLRTexture *clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_);
|
||||
Draw::Framebuffer *depalFBO = framebufferManagerGL_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, Draw::FBO_8888);
|
||||
draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
|
||||
shaderManager_->DirtyLastShader();
|
||||
|
||||
render_->SetScissor(GLRect2D{ 0, 0, (int)framebuffer->renderWidth, (int)framebuffer->renderHeight });
|
||||
render_->SetViewport(GLRViewport{ 0.0f, 0.0f, (float)framebuffer->renderWidth, (float)framebuffer->renderHeight, 0.0f, 1.0f });
|
||||
TextureShaderApplier shaderApply(depal, framebuffer->bufferWidth, framebuffer->bufferHeight, framebuffer->renderWidth, framebuffer->renderHeight);
|
||||
shaderApply.ApplyBounds(gstate_c.vertBounds, gstate_c.curTextureXOffset, gstate_c.curTextureYOffset);
|
||||
shaderApply.Use(drawEngine_);
|
||||
|
||||
glActiveTexture(GL_TEXTURE3);
|
||||
glBindTexture(GL_TEXTURE_2D, clutTexture);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
shaderApply.Use(render_, drawEngine_, shadeInputLayout_);
|
||||
|
||||
framebufferManagerGL_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_SKIP_COPY);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
render_->BindTexture(3, clutTexture);
|
||||
render_->SetTextureSampler(3, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST, 0.0f);
|
||||
|
||||
shaderApply.Shade();
|
||||
shaderApply.Shade(render_);
|
||||
|
||||
draw_->BindFramebufferAsTexture(depalFBO, 0, Draw::FB_COLOR_BIT, 0);
|
||||
|
||||
const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16);
|
||||
const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor;
|
||||
|
||||
TexCacheEntry::TexStatus alphaStatus = CheckAlpha(clutBuf_, getClutDestFormat(clutFormat), clutTotalColors, clutTotalColors, 1);
|
||||
TexCacheEntry::TexStatus alphaStatus = CheckAlpha((const uint8_t *)clutBuf_, getClutDestFormat(clutFormat), clutTotalColors, clutTotalColors, 1);
|
||||
gstate_c.SetTextureFullAlpha(alphaStatus == TexCacheEntry::STATUS_ALPHA_FULL);
|
||||
} else {
|
||||
entry->status &= ~TexCacheEntry::STATUS_DEPALETTIZE;
|
||||
@@ -528,9 +471,10 @@ void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFram
|
||||
framebufferManagerGL_->RebindFramebuffer();
|
||||
SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight);
|
||||
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
|
||||
InvalidateLastTexture();
|
||||
|
||||
// Since we started/ended render passes, might need these.
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE);
|
||||
}
|
||||
|
||||
ReplacedTextureFormat FromGLESFormat(GLenum fmt) {
|
||||
@@ -555,6 +499,10 @@ GLenum ToGLESFormat(ReplacedTextureFormat fmt) {
|
||||
void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImages) {
|
||||
entry->status &= ~TexCacheEntry::STATUS_ALPHA_MASK;
|
||||
|
||||
// Never replace images in-place - there's no such thing, drivers have to fake it anyway, at least if
|
||||
// the image has been in use within the last frame or two.
|
||||
replaceImages = false;
|
||||
|
||||
// For the estimate, we assume cluts always point to 8888 for simplicity.
|
||||
cacheSizeEstimate_ += EstimateTexMemoryUsage(entry);
|
||||
|
||||
@@ -566,7 +514,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag
|
||||
// Always generate a texture name unless it's a framebuffer, we might need it if the texture is replaced later.
|
||||
if (!replaceImages) {
|
||||
if (!entry->textureName) {
|
||||
entry->textureName = AllocTextureName();
|
||||
entry->textureName = render_->CreateTexture(GL_TEXTURE_2D);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -635,8 +583,8 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag
|
||||
if (replaced.GetSize(0, w, h)) {
|
||||
if (replaceImages) {
|
||||
// Since we're replacing the texture, we can't replace the image inside.
|
||||
glDeleteTextures(1, &entry->textureName);
|
||||
entry->textureName = AllocTextureName();
|
||||
render_->DeleteTexture(entry->textureName);
|
||||
entry->textureName = render_->CreateTexture(GL_TEXTURE_2D);
|
||||
replaceImages = false;
|
||||
}
|
||||
|
||||
@@ -670,31 +618,9 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag
|
||||
}
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, entry->textureName);
|
||||
// glBindTexture(GL_TEXTURE_2D, entry->textureName);
|
||||
lastBoundTexture = entry->textureName;
|
||||
|
||||
// Disabled this due to issue #6075: https://github.com/hrydgard/ppsspp/issues/6075
|
||||
// This breaks Dangan Ronpa 2 with mipmapping enabled. Why? No idea, it shouldn't.
|
||||
// glTexStorage2D probably has few benefits for us anyway.
|
||||
if (false && gl_extensions.GLES3 && maxLevel > 0) {
|
||||
// glTexStorage2D requires the use of sized formats.
|
||||
GLenum actualFmt = replaced.Valid() ? ToGLESFormat(replaced.Format(0)) : dstFmt;
|
||||
GLenum storageFmt = GL_RGBA8;
|
||||
switch (actualFmt) {
|
||||
case GL_UNSIGNED_BYTE: storageFmt = GL_RGBA8; break;
|
||||
case GL_UNSIGNED_SHORT_5_6_5: storageFmt = GL_RGB565; break;
|
||||
case GL_UNSIGNED_SHORT_4_4_4_4: storageFmt = GL_RGBA4; break;
|
||||
case GL_UNSIGNED_SHORT_5_5_5_1: storageFmt = GL_RGB5_A1; break;
|
||||
default:
|
||||
ERROR_LOG(G3D, "Unknown dstfmt %i", (int)actualFmt);
|
||||
break;
|
||||
}
|
||||
// TODO: This may cause bugs, since it hard-sets the texture w/h, and we might try to reuse it later with a different size.
|
||||
glTexStorage2D(GL_TEXTURE_2D, maxLevel + 1, storageFmt, w * scaleFactor, h * scaleFactor);
|
||||
// Make sure we don't use glTexImage2D after glTexStorage2D.
|
||||
replaceImages = true;
|
||||
}
|
||||
|
||||
|
||||
// GLES2 doesn't have support for a "Max lod" which is critical as PSP games often
|
||||
// don't specify mips all the way down. As a result, we either need to manually generate
|
||||
// the bottom few levels or rely on OpenGL's autogen mipmaps instead, which might not
|
||||
@@ -709,33 +635,35 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag
|
||||
LoadTextureLevel(*entry, replaced, 0, replaceImages, scaleFactor, dstFmt);
|
||||
|
||||
// Mipmapping only enable when texture scaling disable
|
||||
int texMaxLevel = 0;
|
||||
bool genMips = false;
|
||||
if (maxLevel > 0 && scaleFactor == 1) {
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_TEXTURE_LOD_CONTROL)) {
|
||||
if (badMipSizes) {
|
||||
// WARN_LOG(G3D, "Bad mipmap for texture sized %dx%dx%d - autogenerating", w, h, (int)format);
|
||||
if (canAutoGen) {
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
genMips = true;
|
||||
} else {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
texMaxLevel = 0;
|
||||
maxLevel = 0;
|
||||
}
|
||||
} else {
|
||||
for (int i = 1; i <= maxLevel; i++) {
|
||||
LoadTextureLevel(*entry, replaced, i, replaceImages, scaleFactor, dstFmt);
|
||||
}
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, maxLevel);
|
||||
texMaxLevel = maxLevel;
|
||||
}
|
||||
} else {
|
||||
// Avoid PowerVR driver bug
|
||||
if (canAutoGen && w > 1 && h > 1 && !(h > w && (gl_extensions.bugs & BUG_PVR_GENMIPMAP_HEIGHT_GREATER))) { // Really! only seems to fail if height > width
|
||||
// NOTICE_LOG(G3D, "Generating mipmap for texture sized %dx%d%d", w, h, (int)format);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
genMips = true;
|
||||
} else {
|
||||
maxLevel = 0;
|
||||
}
|
||||
}
|
||||
} else if (gstate_c.Supports(GPU_SUPPORTS_TEXTURE_LOD_CONTROL)) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
texMaxLevel = 0;
|
||||
}
|
||||
|
||||
if (maxLevel == 0) {
|
||||
@@ -747,32 +675,12 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag
|
||||
entry->SetAlphaStatus(TexCacheEntry::TexStatus(replaced.AlphaStatus()));
|
||||
}
|
||||
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_ANISOTROPY)) {
|
||||
int aniso = 1 << g_Config.iAnisotropyLevel;
|
||||
float anisotropyLevel = (float) aniso > maxAnisotropyLevel ? maxAnisotropyLevel : (float) aniso;
|
||||
if (anisotropyLevel > 1.0f) {
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropyLevel);
|
||||
}
|
||||
}
|
||||
render_->FinalizeTexture(entry->textureName, texMaxLevel, genMips);
|
||||
|
||||
// This will rebind it, but that's okay.
|
||||
// Need to actually bind it now - it might only have gotten bound in the init phase.
|
||||
render_->BindTexture(0, entry->textureName);
|
||||
UpdateSamplingParams(*entry, true);
|
||||
|
||||
//glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
//glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
u32 TextureCacheGLES::AllocTextureName() {
|
||||
if (nameCache_.empty()) {
|
||||
nameCache_.resize(TEXCACHE_NAME_CACHE_SIZE);
|
||||
glGenTextures(TEXCACHE_NAME_CACHE_SIZE, &nameCache_[0]);
|
||||
}
|
||||
u32 name = nameCache_.back();
|
||||
nameCache_.pop_back();
|
||||
return name;
|
||||
}
|
||||
|
||||
GLenum TextureCacheGLES::GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const {
|
||||
@@ -797,44 +705,21 @@ GLenum TextureCacheGLES::GetDestFormat(GETextureFormat format, GEPaletteFormat c
|
||||
}
|
||||
}
|
||||
|
||||
void *TextureCacheGLES::DecodeTextureLevelOld(GETextureFormat format, GEPaletteFormat clutformat, int level, GLenum dstFmt, int scaleFactor, int *bufwout) {
|
||||
void *finalBuf = nullptr;
|
||||
u32 texaddr = gstate.getTextureAddress(level);
|
||||
int bufw = GetTextureBufw(level, texaddr, format);
|
||||
if (bufwout)
|
||||
*bufwout = bufw;
|
||||
|
||||
int w = gstate.getTextureWidth(level);
|
||||
int h = gstate.getTextureHeight(level);
|
||||
|
||||
int decPitch = 0;
|
||||
int pixelSize = dstFmt == GL_UNSIGNED_BYTE ? 4 : 2;
|
||||
if (!(scaleFactor == 1 && gstate_c.Supports(GPU_SUPPORTS_UNPACK_SUBIMAGE)) && w != bufw) {
|
||||
decPitch = w * pixelSize;
|
||||
} else {
|
||||
decPitch = bufw * pixelSize;
|
||||
}
|
||||
|
||||
tmpTexBufRearrange_.resize(std::max(w, bufw) * h);
|
||||
DecodeTextureLevel((u8 *)tmpTexBufRearrange_.data(), decPitch, format, clutformat, texaddr, level, bufw, true, false, false);
|
||||
return tmpTexBufRearrange_.data();
|
||||
}
|
||||
|
||||
TexCacheEntry::TexStatus TextureCacheGLES::CheckAlpha(const u32 *pixelData, GLenum dstFmt, int stride, int w, int h) {
|
||||
TexCacheEntry::TexStatus TextureCacheGLES::CheckAlpha(const uint8_t *pixelData, GLenum dstFmt, int stride, int w, int h) {
|
||||
CheckAlphaResult res;
|
||||
switch (dstFmt) {
|
||||
case GL_UNSIGNED_SHORT_4_4_4_4:
|
||||
res = CheckAlphaABGR4444Basic(pixelData, stride, w, h);
|
||||
res = CheckAlphaABGR4444Basic((const uint32_t *)pixelData, stride, w, h);
|
||||
break;
|
||||
case GL_UNSIGNED_SHORT_5_5_5_1:
|
||||
res = CheckAlphaABGR1555Basic(pixelData, stride, w, h);
|
||||
res = CheckAlphaABGR1555Basic((const uint32_t *)pixelData, stride, w, h);
|
||||
break;
|
||||
case GL_UNSIGNED_SHORT_5_6_5:
|
||||
// Never has any alpha.
|
||||
res = CHECKALPHA_FULL;
|
||||
break;
|
||||
default:
|
||||
res = CheckAlphaRGBA8888Basic(pixelData, stride, w, h);
|
||||
res = CheckAlphaRGBA8888Basic((const uint32_t *)pixelData, stride, w, h);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -845,9 +730,7 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r
|
||||
int w = gstate.getTextureWidth(level);
|
||||
int h = gstate.getTextureHeight(level);
|
||||
bool useUnpack = false;
|
||||
u32 *pixelData;
|
||||
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
uint8_t *pixelData;
|
||||
|
||||
// TODO: only do this once
|
||||
u32 texByteAlign = 1;
|
||||
@@ -857,10 +740,10 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r
|
||||
if (replaced.GetSize(level, w, h)) {
|
||||
PROFILE_THIS_SCOPE("replacetex");
|
||||
|
||||
tmpTexBufRearrange_.resize(w * h);
|
||||
int bpp = replaced.Format(level) == ReplacedTextureFormat::F_8888 ? 4 : 2;
|
||||
replaced.Load(level, tmpTexBufRearrange_.data(), bpp * w);
|
||||
pixelData = tmpTexBufRearrange_.data();
|
||||
uint8_t *rearrange = (uint8_t *)AllocateAlignedMemory(w * h * bpp, 16);
|
||||
replaced.Load(level, rearrange, bpp * w);
|
||||
pixelData = rearrange;
|
||||
|
||||
dstFmt = ToGLESFormat(replaced.Format(level));
|
||||
|
||||
@@ -869,22 +752,14 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r
|
||||
PROFILE_THIS_SCOPE("decodetex");
|
||||
|
||||
GEPaletteFormat clutformat = gstate.getClutPaletteFormat();
|
||||
int bufw;
|
||||
void *finalBuf = DecodeTextureLevelOld(GETextureFormat(entry.format), clutformat, level, dstFmt, scaleFactor, &bufw);
|
||||
if (finalBuf == NULL) {
|
||||
return;
|
||||
}
|
||||
u32 texaddr = gstate.getTextureAddress(level);
|
||||
int bufw = GetTextureBufw(level, texaddr, GETextureFormat(entry.format));
|
||||
|
||||
// Can restore these and remove the fixup at the end of DecodeTextureLevel on desktop GL and GLES 3.
|
||||
if (scaleFactor == 1 && gstate_c.Supports(GPU_SUPPORTS_UNPACK_SUBIMAGE) && w != bufw) {
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, bufw);
|
||||
useUnpack = true;
|
||||
}
|
||||
int pixelSize = dstFmt == GL_UNSIGNED_BYTE ? 4 : 2;
|
||||
int decPitch = w * pixelSize;
|
||||
|
||||
// Textures are always aligned to 16 bytes bufw, so this could safely be 4 always.
|
||||
texByteAlign = dstFmt == GL_UNSIGNED_BYTE ? 4 : 2;
|
||||
|
||||
pixelData = (u32 *)finalBuf;
|
||||
pixelData = (uint8_t *)AllocateAlignedMemory(decPitch * h * pixelSize, 16);
|
||||
DecodeTextureLevel(pixelData, decPitch, GETextureFormat(entry.format), clutformat, texaddr, level, bufw, true, false, false);
|
||||
|
||||
// We check before scaling since scaling shouldn't invent alpha from a full alpha texture.
|
||||
if ((entry.status & TexCacheEntry::STATUS_CHANGE_FREQUENT) == 0) {
|
||||
@@ -894,8 +769,15 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r
|
||||
entry.SetAlphaStatus(TexCacheEntry::STATUS_ALPHA_UNKNOWN);
|
||||
}
|
||||
|
||||
if (scaleFactor > 1)
|
||||
scaler.Scale(pixelData, dstFmt, w, h, scaleFactor);
|
||||
if (scaleFactor > 1) {
|
||||
uint8_t *rearrange = (uint8_t *)AllocateAlignedMemory(w * scaleFactor * h * scaleFactor * 4, 16);
|
||||
scaler.ScaleAlways((u32 *)rearrange, (u32 *)pixelData, dstFmt, w, h, scaleFactor);
|
||||
FreeAlignedMemory(pixelData);
|
||||
pixelData = rearrange;
|
||||
}
|
||||
|
||||
// Textures are always aligned to 16 bytes bufw, so this could safely be 4 always.
|
||||
texByteAlign = dstFmt == GL_UNSIGNED_BYTE ? 4 : 2;
|
||||
|
||||
if (replacer_.Enabled()) {
|
||||
ReplacedTextureDecodeInfo replacedInfo;
|
||||
@@ -912,133 +794,21 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r
|
||||
}
|
||||
}
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, texByteAlign);
|
||||
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
|
||||
GLuint components = dstFmt == GL_UNSIGNED_SHORT_5_6_5 ? GL_RGB : GL_RGBA;
|
||||
|
||||
GLuint components2 = components;
|
||||
|
||||
if (replaceImages) {
|
||||
PROFILE_THIS_SCOPE("repltex");
|
||||
glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, w, h, components2, dstFmt, pixelData);
|
||||
Crash();
|
||||
// glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, w, h, components2, dstFmt, pixelData);
|
||||
} else {
|
||||
PROFILE_THIS_SCOPE("loadtex");
|
||||
// Avoid misleading errors in texture upload, these are common.
|
||||
GLenum err = glGetError();
|
||||
if (err) {
|
||||
WARN_LOG(G3D, "Got an error BEFORE texture upload: %08x (%s)", err, GLEnumToString(err).c_str());
|
||||
}
|
||||
if (IsFakeMipmapChange())
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, components, w, h, 0, components2, dstFmt, pixelData);
|
||||
render_->TextureImage(entry.textureName, 0, w, h, components, components2, dstFmt, pixelData, GLRAllocType::ALIGNED);
|
||||
else
|
||||
glTexImage2D(GL_TEXTURE_2D, level, components, w, h, 0, components2, dstFmt, pixelData);
|
||||
if (!lowMemoryMode_) {
|
||||
// TODO: We really, really should avoid calling glGetError.
|
||||
GLenum err = glGetError();
|
||||
if (err == GL_OUT_OF_MEMORY) {
|
||||
WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode");
|
||||
lowMemoryMode_ = true;
|
||||
decimationCounter_ = 0;
|
||||
Decimate();
|
||||
// Try again, now that we've cleared out textures in lowMemoryMode_.
|
||||
glTexImage2D(GL_TEXTURE_2D, level, components, w, h, 0, components2, dstFmt, pixelData);
|
||||
|
||||
I18NCategory *err = GetI18NCategory("Error");
|
||||
if (scaleFactor > 1) {
|
||||
host->NotifyUserMessage(err->T("Warning: Video memory FULL, reducing upscaling and switching to slow caching mode"), 2.0f);
|
||||
} else {
|
||||
host->NotifyUserMessage(err->T("Warning: Video memory FULL, switching to slow caching mode"), 2.0f);
|
||||
}
|
||||
} else if (err != GL_NO_ERROR) {
|
||||
// We checked the err anyway, might as well log if there is one.
|
||||
WARN_LOG(G3D, "Got an error in texture upload: %08x (%s) (components=%s components2=%s dstFmt=%s w=%d h=%d level=%d)",
|
||||
err, GLEnumToString(err).c_str(), GLEnumToString(components).c_str(), GLEnumToString(components2).c_str(), GLEnumToString(dstFmt).c_str(),
|
||||
w, h, level);
|
||||
}
|
||||
}
|
||||
render_->TextureImage(entry.textureName, level, w, h, components, components2, dstFmt, pixelData, GLRAllocType::ALIGNED);
|
||||
}
|
||||
|
||||
if (useUnpack) {
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Only used by Qt UI?
|
||||
bool TextureCacheGLES::DecodeTexture(u8* output, const GPUgstate &state) {
|
||||
GPUgstate oldState = gstate;
|
||||
gstate = state;
|
||||
|
||||
u32 texaddr = gstate.getTextureAddress(0);
|
||||
|
||||
if (!Memory::IsValidAddress(texaddr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GLenum dstFmt = 0;
|
||||
|
||||
GETextureFormat format = gstate.getTextureFormat();
|
||||
GEPaletteFormat clutformat = gstate.getClutPaletteFormat();
|
||||
u8 level = 0;
|
||||
|
||||
int bufw = GetTextureBufw(level, texaddr, format);
|
||||
int w = gstate.getTextureWidth(level);
|
||||
int h = gstate.getTextureHeight(level);
|
||||
|
||||
void *finalBuf = DecodeTextureLevelOld(format, clutformat, level, dstFmt, 1);
|
||||
if (finalBuf == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (dstFmt) {
|
||||
case GL_UNSIGNED_SHORT_4_4_4_4:
|
||||
for (int y = 0; y < h; y++)
|
||||
for (int x = 0; x < bufw; x++) {
|
||||
u32 val = ((u16*)finalBuf)[y*bufw + x];
|
||||
u32 r = ((val>>12) & 0xF) * 17;
|
||||
u32 g = ((val>> 8) & 0xF) * 17;
|
||||
u32 b = ((val>> 4) & 0xF) * 17;
|
||||
u32 a = ((val>> 0) & 0xF) * 17;
|
||||
((u32*)output)[y*w + x] = (a << 24) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
break;
|
||||
|
||||
case GL_UNSIGNED_SHORT_5_5_5_1:
|
||||
for (int y = 0; y < h; y++)
|
||||
for (int x = 0; x < bufw; x++) {
|
||||
u32 val = ((u16*)finalBuf)[y*bufw + x];
|
||||
u32 r = Convert5To8((val>>11) & 0x1F);
|
||||
u32 g = Convert5To8((val>> 6) & 0x1F);
|
||||
u32 b = Convert5To8((val>> 1) & 0x1F);
|
||||
u32 a = (val & 0x1) * 255;
|
||||
((u32*)output)[y*w + x] = (a << 24) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
break;
|
||||
|
||||
case GL_UNSIGNED_SHORT_5_6_5:
|
||||
for (int y = 0; y < h; y++)
|
||||
for (int x = 0; x < bufw; x++) {
|
||||
u32 val = ((u16*)finalBuf)[y*bufw + x];
|
||||
u32 a = 0xFF;
|
||||
u32 r = Convert5To8((val>>11) & 0x1F);
|
||||
u32 g = Convert6To8((val>> 5) & 0x3F);
|
||||
u32 b = Convert5To8((val ) & 0x1F);
|
||||
((u32*)output)[y*w + x] = (a << 24) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
for (int y = 0; y < h; y++)
|
||||
for (int x = 0; x < bufw; x++) {
|
||||
u32 val = ((u32*)finalBuf)[y*bufw + x];
|
||||
((u32*)output)[y*w + x] = ((val & 0xFF000000)) | ((val & 0x00FF0000)>>16) | ((val & 0x0000FF00)) | ((val & 0x000000FF)<<16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
gstate = oldState;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureCacheGLES::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) {
|
||||
@@ -1054,19 +824,40 @@ bool TextureCacheGLES::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level)
|
||||
}
|
||||
|
||||
SetTexture(true);
|
||||
if (!nextTexture_)
|
||||
return false;
|
||||
|
||||
// Apply texture may need to rebuild the texture if we're about to render, or bind a framebuffer.
|
||||
TexCacheEntry *entry = nextTexture_;
|
||||
ApplyTexture();
|
||||
|
||||
// TODO: Centralize?
|
||||
if (entry->framebuffer) {
|
||||
VirtualFramebuffer *vfb = entry->framebuffer;
|
||||
buffer.Allocate(vfb->bufferWidth, vfb->bufferHeight, GPU_DBG_FORMAT_8888, false);
|
||||
bool retval = draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, 0, vfb->bufferWidth, vfb->bufferHeight, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), vfb->bufferWidth);
|
||||
// Vulkan requires us to re-apply all dynamic state for each command buffer, and the above will cause us to start a new cmdbuf.
|
||||
// So let's dirty the things that are involved in Vulkan dynamic state. Readbacks are not frequent so this won't hurt other backends.
|
||||
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE);
|
||||
// We may have blitted to a temp FBO.
|
||||
framebufferManager_->RebindFramebuffer();
|
||||
return retval;
|
||||
}
|
||||
|
||||
GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
|
||||
// Not a framebuffer, so let's assume these are right.
|
||||
int w = gstate.getTextureWidth(level);
|
||||
int h = gstate.getTextureHeight(level);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h);
|
||||
|
||||
if (level != 0) {
|
||||
gstate = saved;
|
||||
}
|
||||
|
||||
buffer.Allocate(w, h, GE_FORMAT_8888, false);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.GetData());
|
||||
renderManager->CopyImageToMemorySync(entry->textureName, level, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, (uint8_t *)buffer.GetData(), w);
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);
|
||||
framebufferManager_->RebindFramebuffer();
|
||||
|
||||
return true;
|
||||
#else
|
||||
|
||||
+10
-12
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "gfx_es2/gpu_features.h"
|
||||
#include "gfx/gl_common.h"
|
||||
#include "thin3d/GLRenderManager.h"
|
||||
#include "GPU/GPUInterface.h"
|
||||
#include "GPU/GPUState.h"
|
||||
#include "GPU/GLES/TextureScalerGLES.h"
|
||||
@@ -31,6 +32,7 @@ class FramebufferManagerGLES;
|
||||
class DepalShaderCacheGLES;
|
||||
class ShaderManagerGLES;
|
||||
class DrawEngineGLES;
|
||||
class GLRTexture;
|
||||
|
||||
class TextureCacheGLES : public TextureCacheCommon {
|
||||
public:
|
||||
@@ -52,20 +54,15 @@ public:
|
||||
}
|
||||
|
||||
void ForgetLastTexture() override {
|
||||
lastBoundTexture = INVALID_TEX;
|
||||
lastBoundTexture = nullptr;
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);
|
||||
}
|
||||
void InvalidateLastTexture(TexCacheEntry *entry = nullptr) override {
|
||||
if (!entry || entry->textureName == lastBoundTexture) {
|
||||
lastBoundTexture = INVALID_TEX;
|
||||
lastBoundTexture = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
u32 AllocTextureName();
|
||||
|
||||
// Only used by Qt UI?
|
||||
bool DecodeTexture(u8 *output, const GPUgstate &state);
|
||||
|
||||
void SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight);
|
||||
bool GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) override;
|
||||
|
||||
@@ -80,25 +77,26 @@ private:
|
||||
void UpdateSamplingParams(TexCacheEntry &entry, bool force);
|
||||
void LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &replaced, int level, bool replaceImages, int scaleFactor, GLenum dstFmt);
|
||||
GLenum GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const;
|
||||
void *DecodeTextureLevelOld(GETextureFormat format, GEPaletteFormat clutformat, int level, GLenum dstFmt, int scaleFactor, int *bufw = 0);
|
||||
TexCacheEntry::TexStatus CheckAlpha(const u32 *pixelData, GLenum dstFmt, int stride, int w, int h);
|
||||
|
||||
TexCacheEntry::TexStatus CheckAlpha(const uint8_t *pixelData, GLenum dstFmt, int stride, int w, int h);
|
||||
void UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutBase, bool clutIndexIsSimple) override;
|
||||
void ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer) override;
|
||||
|
||||
void BuildTexture(TexCacheEntry *const entry, bool replaceImages) override;
|
||||
|
||||
std::vector<u32> nameCache_;
|
||||
GLRenderManager *render_;
|
||||
|
||||
TextureScalerGLES scaler;
|
||||
|
||||
u32 lastBoundTexture;
|
||||
float maxAnisotropyLevel;
|
||||
GLRTexture *lastBoundTexture;
|
||||
|
||||
FramebufferManagerGLES *framebufferManagerGL_;
|
||||
DepalShaderCacheGLES *depalShaderCache_;
|
||||
ShaderManagerGLES *shaderManager_;
|
||||
DrawEngineGLES *drawEngine_;
|
||||
|
||||
GLRInputLayout *shadeInputLayout_;
|
||||
|
||||
enum { INVALID_TEX = -1 };
|
||||
};
|
||||
|
||||
|
||||
@@ -39,30 +39,6 @@
|
||||
|
||||
#define WRITE p+=sprintf
|
||||
|
||||
static const char * const boneWeightAttrDecl[9] = {
|
||||
"#ERROR#",
|
||||
"attribute mediump float w1;\n",
|
||||
"attribute mediump vec2 w1;\n",
|
||||
"attribute mediump vec3 w1;\n",
|
||||
"attribute mediump vec4 w1;\n",
|
||||
"attribute mediump vec4 w1;\nattribute mediump float w2;\n",
|
||||
"attribute mediump vec4 w1;\nattribute mediump vec2 w2;\n",
|
||||
"attribute mediump vec4 w1;\nattribute mediump vec3 w2;\n",
|
||||
"attribute mediump vec4 w1, w2;\n",
|
||||
};
|
||||
|
||||
static const char * const boneWeightInDecl[9] = {
|
||||
"#ERROR#",
|
||||
"in mediump float w1;\n",
|
||||
"in mediump vec2 w1;\n",
|
||||
"in mediump vec3 w1;\n",
|
||||
"in mediump vec4 w1;\n",
|
||||
"in mediump vec4 w1;\nin mediump float w2;\n",
|
||||
"in mediump vec4 w1;\nin mediump vec2 w2;\n",
|
||||
"in mediump vec4 w1;\nin mediump vec3 w2;\n",
|
||||
"in mediump vec4 w1, w2;\n",
|
||||
};
|
||||
|
||||
enum DoLightComputation {
|
||||
LIGHT_OFF,
|
||||
LIGHT_SHADE,
|
||||
@@ -113,9 +89,7 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask,
|
||||
bool glslES30 = false;
|
||||
const char *varying = "varying";
|
||||
const char *attribute = "attribute";
|
||||
const char * const * boneWeightDecl = boneWeightAttrDecl;
|
||||
const char *texelFetch = NULL;
|
||||
bool isAllowTexture1D = false;
|
||||
bool highpFog = false;
|
||||
bool highpTexcoord = false;
|
||||
|
||||
@@ -143,13 +117,11 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask,
|
||||
glslES30 = true;
|
||||
WRITE(p, "#version 330\n");
|
||||
texelFetch = "texelFetch";
|
||||
isAllowTexture1D = true;
|
||||
} else if (gl_extensions.VersionGEThan(3, 0, 0)) {
|
||||
WRITE(p, "#version 130\n");
|
||||
if (gl_extensions.EXT_gpu_shader4) {
|
||||
WRITE(p, "#extension GL_EXT_gpu_shader4 : enable\n");
|
||||
texelFetch = "texelFetch";
|
||||
isAllowTexture1D = true;
|
||||
}
|
||||
} else {
|
||||
WRITE(p, "#version 110\n");
|
||||
@@ -169,7 +141,6 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask,
|
||||
if (glslES30 || gl_extensions.IsCoreContext) {
|
||||
attribute = "in";
|
||||
varying = "out";
|
||||
boneWeightDecl = boneWeightInDecl;
|
||||
}
|
||||
|
||||
bool isModeThrough = id.Bit(VS_BIT_IS_THROUGH);
|
||||
@@ -193,7 +164,6 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask,
|
||||
bool flipNormal = id.Bit(VS_BIT_NORM_REVERSE);
|
||||
int ls0 = id.Bits(VS_BIT_LS0, 2);
|
||||
int ls1 = id.Bits(VS_BIT_LS1, 2);
|
||||
bool enableBones = id.Bit(VS_BIT_ENABLE_BONES);
|
||||
bool enableLighting = id.Bit(VS_BIT_LIGHTING_ENABLE);
|
||||
int matUpdate = id.Bits(VS_BIT_MATERIAL_UPDATE, 3);
|
||||
|
||||
@@ -219,16 +189,6 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask,
|
||||
}
|
||||
}
|
||||
|
||||
int numBoneWeights = 0;
|
||||
int boneWeightScale = id.Bits(VS_BIT_WEIGHT_FMTSCALE, 2);
|
||||
if (enableBones) {
|
||||
numBoneWeights = 1 + id.Bits(VS_BIT_BONES, 3);
|
||||
WRITE(p, "%s", boneWeightDecl[numBoneWeights]);
|
||||
*attrMask |= 1 << ATTR_W1;
|
||||
if (numBoneWeights >= 5)
|
||||
*attrMask |= 1 << ATTR_W2;
|
||||
}
|
||||
|
||||
if (useHWTransform)
|
||||
WRITE(p, "%s vec3 position;\n", attribute);
|
||||
else
|
||||
@@ -281,17 +241,6 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask,
|
||||
WRITE(p, "uniform mediump mat4 u_texmtx;\n");
|
||||
*uniformMask |= DIRTY_TEXMATRIX;
|
||||
}
|
||||
if (enableBones) {
|
||||
#ifdef USE_BONE_ARRAY
|
||||
WRITE(p, "uniform mediump mat4 u_bone[%i];\n", numBoneWeights);
|
||||
*uniformMask |= DIRTY_BONE_UNIFORMS;
|
||||
#else
|
||||
for (int i = 0; i < numBoneWeights; i++) {
|
||||
WRITE(p, "uniform mat4 u_bone%i;\n", i);
|
||||
*uniformMask |= DIRTY_BONEMATRIX0 << i;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (doTexture) {
|
||||
WRITE(p, "uniform vec4 u_uvscaleoffset;\n");
|
||||
*uniformMask |= DIRTY_UVSCALEOFFSET;
|
||||
@@ -312,8 +261,7 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask,
|
||||
|
||||
if (type == GE_LIGHTTYPE_SPOT || type == GE_LIGHTTYPE_UNKNOWN) {
|
||||
WRITE(p, "uniform mediump vec3 u_lightdir%i;\n", i);
|
||||
WRITE(p, "uniform mediump float u_lightangle%i;\n", i);
|
||||
WRITE(p, "uniform mediump float u_lightspotCoef%i;\n", i);
|
||||
WRITE(p, "uniform mediump vec2 u_lightangle_spotCoef%i;\n", i);
|
||||
}
|
||||
WRITE(p, "uniform lowp vec3 u_lightambient%i;\n", i);
|
||||
WRITE(p, "uniform lowp vec3 u_lightdiffuse%i;\n", i);
|
||||
@@ -387,10 +335,9 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask,
|
||||
if (doBezier || doSpline) {
|
||||
*uniformMask |= DIRTY_BEZIERSPLINE;
|
||||
|
||||
const char *sampler = !isAllowTexture1D ? "sampler2D" : "sampler1D";
|
||||
WRITE(p, "uniform %s u_tess_pos_tex;\n", sampler);
|
||||
WRITE(p, "uniform %s u_tess_tex_tex;\n", sampler);
|
||||
WRITE(p, "uniform %s u_tess_col_tex;\n", sampler);
|
||||
WRITE(p, "uniform sampler2D u_tess_pos_tex;\n");
|
||||
WRITE(p, "uniform sampler2D u_tess_tex_tex;\n");
|
||||
WRITE(p, "uniform sampler2D u_tess_col_tex;\n");
|
||||
|
||||
WRITE(p, "uniform int u_spline_count_u;\n");
|
||||
|
||||
@@ -504,190 +451,111 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask,
|
||||
WriteGuardBand(p);
|
||||
WRITE(p, " gl_Position = outPos;\n");
|
||||
} else {
|
||||
// Step 1: World Transform / Skinning
|
||||
if (!enableBones) {
|
||||
// Hardware tessellation
|
||||
if (doBezier || doSpline) {
|
||||
WRITE(p, " vec3 _pos[16];\n");
|
||||
WRITE(p, " vec2 _tex[16];\n");
|
||||
WRITE(p, " vec4 _col[16];\n");
|
||||
WRITE(p, " int num_patches_u = %s;\n", doBezier ? "(u_spline_count_u - 1) / 3" : "u_spline_count_u - 3");
|
||||
WRITE(p, " int u = int(mod(float(gl_InstanceID), float(num_patches_u)));\n");
|
||||
WRITE(p, " int v = gl_InstanceID / num_patches_u;\n");
|
||||
WRITE(p, " ivec2 patch_pos = ivec2(u, v);\n");
|
||||
WRITE(p, " for (int i = 0; i < 4; i++) {\n");
|
||||
WRITE(p, " for (int j = 0; j < 4; j++) {\n");
|
||||
WRITE(p, " int index = (i + v%s) * u_spline_count_u + (j + u%s);\n", doBezier ? " * 3" : "", doBezier ? " * 3" : "");
|
||||
const char *index = !isAllowTexture1D ? "ivec2(index, 0)" : "index";
|
||||
WRITE(p, " _pos[i * 4 + j] = %s(u_tess_pos_tex, %s, 0).xyz;\n", texelFetch, index);
|
||||
if (doTexture && hasTexcoord && hasTexcoordTess)
|
||||
WRITE(p, " _tex[i * 4 + j] = %s(u_tess_tex_tex, %s, 0).xy;\n", texelFetch, index);
|
||||
if (hasColor && hasColorTess)
|
||||
WRITE(p, " _col[i * 4 + j] = %s(u_tess_col_tex, %s, 0).rgba;\n", texelFetch, index);
|
||||
WRITE(p, " }\n");
|
||||
WRITE(p, " }\n");
|
||||
WRITE(p, " vec2 tess_pos = position.xy;\n");
|
||||
WRITE(p, " vec2 weights[4];\n");
|
||||
if (doBezier) {
|
||||
// Bernstein 3D
|
||||
WRITE(p, " weights[0] = (1.0 - tess_pos) * (1.0 - tess_pos) * (1.0 - tess_pos);\n");
|
||||
WRITE(p, " weights[1] = 3.0 * tess_pos * (1.0 - tess_pos) * (1.0 - tess_pos);\n");
|
||||
WRITE(p, " weights[2] = 3.0 * tess_pos * tess_pos * (1.0 - tess_pos);\n");
|
||||
WRITE(p, " weights[3] = tess_pos * tess_pos * tess_pos;\n");
|
||||
} else { // Spline
|
||||
WRITE(p, " ivec2 spline_num_patches = ivec2(u_spline_count_u - 3, u_spline_count_v - 3);\n");
|
||||
WRITE(p, " ivec2 spline_type = ivec2(u_spline_type_u, u_spline_type_v);\n");
|
||||
WRITE(p, " vec2 knots[6];\n");
|
||||
WRITE(p, " spline_knot(spline_num_patches, spline_type, knots, patch_pos);\n");
|
||||
WRITE(p, " spline_weight(tess_pos + vec2(patch_pos), knots, weights);\n");
|
||||
}
|
||||
WRITE(p, " vec3 pos = tess_sample(_pos, weights);\n");
|
||||
if (doTexture && hasTexcoord) {
|
||||
if (hasTexcoordTess)
|
||||
WRITE(p, " vec2 tex = tess_sample(_tex, weights);\n");
|
||||
else
|
||||
WRITE(p, " vec2 tex = tess_pos + vec2(patch_pos);\n");
|
||||
}
|
||||
if (hasColor) {
|
||||
if (hasColorTess)
|
||||
WRITE(p, " vec4 col = tess_sample(_col, weights);\n");
|
||||
else
|
||||
WRITE(p, " vec4 col = %s(u_tess_col_tex, %s, 0).rgba;\n", texelFetch, !isAllowTexture1D ? "ivec2(0, 0)" : "0");
|
||||
}
|
||||
if (hasNormal) {
|
||||
// Curved surface is probably always need to compute normal(not sampling from control points)
|
||||
if (doBezier) {
|
||||
// Bernstein derivative
|
||||
WRITE(p, " vec2 bernderiv[4];\n");
|
||||
WRITE(p, " bernderiv[0] = -3.0 * (tess_pos - 1.0) * (tess_pos - 1.0); \n");
|
||||
WRITE(p, " bernderiv[1] = 9.0 * tess_pos * tess_pos - 12.0 * tess_pos + 3.0; \n");
|
||||
WRITE(p, " bernderiv[2] = 3.0 * (2.0 - 3.0 * tess_pos) * tess_pos; \n");
|
||||
WRITE(p, " bernderiv[3] = 3.0 * tess_pos * tess_pos; \n");
|
||||
|
||||
WRITE(p, " vec2 bernderiv_u[4];\n");
|
||||
WRITE(p, " vec2 bernderiv_v[4];\n");
|
||||
WRITE(p, " for (int i = 0; i < 4; i++) {\n");
|
||||
WRITE(p, " bernderiv_u[i] = vec2(bernderiv[i].x, weights[i].y);\n");
|
||||
WRITE(p, " bernderiv_v[i] = vec2(weights[i].x, bernderiv[i].y);\n");
|
||||
WRITE(p, " }\n");
|
||||
|
||||
WRITE(p, " vec3 du = tess_sample(_pos, bernderiv_u);\n");
|
||||
WRITE(p, " vec3 dv = tess_sample(_pos, bernderiv_v);\n");
|
||||
} else { // Spline
|
||||
WRITE(p, " vec2 tess_next_u = vec2(normal.x, 0.0);\n");
|
||||
WRITE(p, " vec2 tess_next_v = vec2(0.0, normal.y);\n");
|
||||
// Right
|
||||
WRITE(p, " vec2 tess_pos_r = tess_pos + tess_next_u;\n");
|
||||
WRITE(p, " spline_weight(tess_pos_r + vec2(patch_pos), knots, weights);\n");
|
||||
WRITE(p, " vec3 pos_r = tess_sample(_pos, weights);\n");
|
||||
// Left
|
||||
WRITE(p, " vec2 tess_pos_l = tess_pos - tess_next_u;\n");
|
||||
WRITE(p, " spline_weight(tess_pos_l + vec2(patch_pos), knots, weights);\n");
|
||||
WRITE(p, " vec3 pos_l = tess_sample(_pos, weights);\n");
|
||||
// Down
|
||||
WRITE(p, " vec2 tess_pos_d = tess_pos + tess_next_v;\n");
|
||||
WRITE(p, " spline_weight(tess_pos_d + vec2(patch_pos), knots, weights);\n");
|
||||
WRITE(p, " vec3 pos_d = tess_sample(_pos, weights);\n");
|
||||
// Up
|
||||
WRITE(p, " vec2 tess_pos_u = tess_pos - tess_next_v;\n");
|
||||
WRITE(p, " spline_weight(tess_pos_u + vec2(patch_pos), knots, weights);\n");
|
||||
WRITE(p, " vec3 pos_u = tess_sample(_pos, weights);\n");
|
||||
|
||||
WRITE(p, " vec3 du = pos_r - pos_l;\n");
|
||||
WRITE(p, " vec3 dv = pos_d - pos_u;\n");
|
||||
}
|
||||
WRITE(p, " vec3 nrm = cross(du, dv);\n");
|
||||
WRITE(p, " nrm = normalize(nrm);\n");
|
||||
}
|
||||
WRITE(p, " vec3 worldpos = (u_world * vec4(pos.xyz, 1.0)).xyz;\n");
|
||||
if (hasNormal) {
|
||||
WRITE(p, " mediump vec3 worldnormal = normalize((u_world * vec4(%snrm, 0.0)).xyz);\n", flipNormalTess ? "-" : "");
|
||||
} else {
|
||||
WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n");
|
||||
}
|
||||
} else {
|
||||
// No skinning, just standard T&L.
|
||||
WRITE(p, " vec3 worldpos = (u_world * vec4(position.xyz, 1.0)).xyz;\n");
|
||||
if (hasNormal)
|
||||
WRITE(p, " mediump vec3 worldnormal = normalize((u_world * vec4(%snormal, 0.0)).xyz);\n", flipNormal ? "-" : "");
|
||||
// Step 1: World Transform
|
||||
// Hardware tessellation
|
||||
if (doBezier || doSpline) {
|
||||
WRITE(p, " vec3 _pos[16];\n");
|
||||
WRITE(p, " vec2 _tex[16];\n");
|
||||
WRITE(p, " vec4 _col[16];\n");
|
||||
WRITE(p, " int num_patches_u = %s;\n", doBezier ? "(u_spline_count_u - 1) / 3" : "u_spline_count_u - 3");
|
||||
WRITE(p, " int u = int(mod(float(gl_InstanceID), float(num_patches_u)));\n");
|
||||
WRITE(p, " int v = gl_InstanceID / num_patches_u;\n");
|
||||
WRITE(p, " ivec2 patch_pos = ivec2(u, v);\n");
|
||||
WRITE(p, " for (int i = 0; i < 4; i++) {\n");
|
||||
WRITE(p, " for (int j = 0; j < 4; j++) {\n");
|
||||
WRITE(p, " int index = (i + v%s) * u_spline_count_u + (j + u%s);\n", doBezier ? " * 3" : "", doBezier ? " * 3" : "");
|
||||
WRITE(p, " _pos[i * 4 + j] = %s(u_tess_pos_tex, ivec2(index, 0), 0).xyz;\n", texelFetch);
|
||||
if (doTexture && hasTexcoord && hasTexcoordTess)
|
||||
WRITE(p, " _tex[i * 4 + j] = %s(u_tess_tex_tex, ivec2(index, 0), 0).xy;\n", texelFetch);
|
||||
if (hasColor && hasColorTess)
|
||||
WRITE(p, " _col[i * 4 + j] = %s(u_tess_col_tex, ivec2(index, 0), 0).rgba;\n", texelFetch);
|
||||
WRITE(p, " }\n");
|
||||
WRITE(p, " }\n");
|
||||
WRITE(p, " vec2 tess_pos = position.xy;\n");
|
||||
WRITE(p, " vec2 weights[4];\n");
|
||||
if (doBezier) {
|
||||
// Bernstein 3D
|
||||
WRITE(p, " weights[0] = (1.0 - tess_pos) * (1.0 - tess_pos) * (1.0 - tess_pos);\n");
|
||||
WRITE(p, " weights[1] = 3.0 * tess_pos * (1.0 - tess_pos) * (1.0 - tess_pos);\n");
|
||||
WRITE(p, " weights[2] = 3.0 * tess_pos * tess_pos * (1.0 - tess_pos);\n");
|
||||
WRITE(p, " weights[3] = tess_pos * tess_pos * tess_pos;\n");
|
||||
} else { // Spline
|
||||
WRITE(p, " ivec2 spline_num_patches = ivec2(u_spline_count_u - 3, u_spline_count_v - 3);\n");
|
||||
WRITE(p, " ivec2 spline_type = ivec2(u_spline_type_u, u_spline_type_v);\n");
|
||||
WRITE(p, " vec2 knots[6];\n");
|
||||
WRITE(p, " spline_knot(spline_num_patches, spline_type, knots, patch_pos);\n");
|
||||
WRITE(p, " spline_weight(tess_pos + vec2(patch_pos), knots, weights);\n");
|
||||
}
|
||||
WRITE(p, " vec3 pos = tess_sample(_pos, weights);\n");
|
||||
if (doTexture && hasTexcoord) {
|
||||
if (hasTexcoordTess)
|
||||
WRITE(p, " vec2 tex = tess_sample(_tex, weights);\n");
|
||||
else
|
||||
WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n");
|
||||
WRITE(p, " vec2 tex = tess_pos + vec2(patch_pos);\n");
|
||||
}
|
||||
if (hasColor) {
|
||||
if (hasColorTess)
|
||||
WRITE(p, " vec4 col = tess_sample(_col, weights);\n");
|
||||
else
|
||||
WRITE(p, " vec4 col = %s(u_tess_col_tex, ivec2(0, 0), 0).rgba;\n", texelFetch);
|
||||
}
|
||||
if (hasNormal) {
|
||||
// Curved surface is probably always need to compute normal(not sampling from control points)
|
||||
if (doBezier) {
|
||||
// Bernstein derivative
|
||||
WRITE(p, " vec2 bernderiv[4];\n");
|
||||
WRITE(p, " bernderiv[0] = -3.0 * (tess_pos - 1.0) * (tess_pos - 1.0); \n");
|
||||
WRITE(p, " bernderiv[1] = 9.0 * tess_pos * tess_pos - 12.0 * tess_pos + 3.0; \n");
|
||||
WRITE(p, " bernderiv[2] = 3.0 * (2.0 - 3.0 * tess_pos) * tess_pos; \n");
|
||||
WRITE(p, " bernderiv[3] = 3.0 * tess_pos * tess_pos; \n");
|
||||
|
||||
WRITE(p, " vec2 bernderiv_u[4];\n");
|
||||
WRITE(p, " vec2 bernderiv_v[4];\n");
|
||||
WRITE(p, " for (int i = 0; i < 4; i++) {\n");
|
||||
WRITE(p, " bernderiv_u[i] = vec2(bernderiv[i].x, weights[i].y);\n");
|
||||
WRITE(p, " bernderiv_v[i] = vec2(weights[i].x, bernderiv[i].y);\n");
|
||||
WRITE(p, " }\n");
|
||||
|
||||
WRITE(p, " vec3 du = tess_sample(_pos, bernderiv_u);\n");
|
||||
WRITE(p, " vec3 dv = tess_sample(_pos, bernderiv_v);\n");
|
||||
} else { // Spline
|
||||
WRITE(p, " vec2 tess_next_u = vec2(normal.x, 0.0);\n");
|
||||
WRITE(p, " vec2 tess_next_v = vec2(0.0, normal.y);\n");
|
||||
// Right
|
||||
WRITE(p, " vec2 tess_pos_r = tess_pos + tess_next_u;\n");
|
||||
WRITE(p, " spline_weight(tess_pos_r + vec2(patch_pos), knots, weights);\n");
|
||||
WRITE(p, " vec3 pos_r = tess_sample(_pos, weights);\n");
|
||||
// Left
|
||||
WRITE(p, " vec2 tess_pos_l = tess_pos - tess_next_u;\n");
|
||||
WRITE(p, " spline_weight(tess_pos_l + vec2(patch_pos), knots, weights);\n");
|
||||
WRITE(p, " vec3 pos_l = tess_sample(_pos, weights);\n");
|
||||
// Down
|
||||
WRITE(p, " vec2 tess_pos_d = tess_pos + tess_next_v;\n");
|
||||
WRITE(p, " spline_weight(tess_pos_d + vec2(patch_pos), knots, weights);\n");
|
||||
WRITE(p, " vec3 pos_d = tess_sample(_pos, weights);\n");
|
||||
// Up
|
||||
WRITE(p, " vec2 tess_pos_u = tess_pos - tess_next_v;\n");
|
||||
WRITE(p, " spline_weight(tess_pos_u + vec2(patch_pos), knots, weights);\n");
|
||||
WRITE(p, " vec3 pos_u = tess_sample(_pos, weights);\n");
|
||||
|
||||
WRITE(p, " vec3 du = pos_r - pos_l;\n");
|
||||
WRITE(p, " vec3 dv = pos_d - pos_u;\n");
|
||||
}
|
||||
WRITE(p, " vec3 nrm = cross(du, dv);\n");
|
||||
WRITE(p, " nrm = normalize(nrm);\n");
|
||||
}
|
||||
WRITE(p, " vec3 worldpos = (u_world * vec4(pos.xyz, 1.0)).xyz;\n");
|
||||
if (hasNormal) {
|
||||
WRITE(p, " mediump vec3 worldnormal = normalize((u_world * vec4(%snrm, 0.0)).xyz);\n", flipNormalTess ? "-" : "");
|
||||
} else {
|
||||
WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n");
|
||||
}
|
||||
} else {
|
||||
static const char *rescale[4] = {"", " * 1.9921875", " * 1.999969482421875", ""}; // 2*127.5f/128.f, 2*32767.5f/32768.f, 1.0f};
|
||||
const char *factor = rescale[boneWeightScale];
|
||||
|
||||
static const char * const boneWeightAttr[8] = {
|
||||
"w1.x", "w1.y", "w1.z", "w1.w",
|
||||
"w2.x", "w2.y", "w2.z", "w2.w",
|
||||
};
|
||||
|
||||
#if defined(USE_FOR_LOOP) && defined(USE_BONE_ARRAY)
|
||||
|
||||
// To loop through the weights, we unfortunately need to put them in a float array.
|
||||
// GLSL ES sucks - no way to directly initialize an array!
|
||||
switch (numBoneWeights) {
|
||||
case 1: WRITE(p, " float w[1]; w[0] = w1;\n"); break;
|
||||
case 2: WRITE(p, " float w[2]; w[0] = w1.x; w[1] = w1.y;\n"); break;
|
||||
case 3: WRITE(p, " float w[3]; w[0] = w1.x; w[1] = w1.y; w[2] = w1.z;\n"); break;
|
||||
case 4: WRITE(p, " float w[4]; w[0] = w1.x; w[1] = w1.y; w[2] = w1.z; w[3] = w1.w;\n"); break;
|
||||
case 5: WRITE(p, " float w[5]; w[0] = w1.x; w[1] = w1.y; w[2] = w1.z; w[3] = w1.w; w[4] = w2;\n"); break;
|
||||
case 6: WRITE(p, " float w[6]; w[0] = w1.x; w[1] = w1.y; w[2] = w1.z; w[3] = w1.w; w[4] = w2.x; w[5] = w2.y;\n"); break;
|
||||
case 7: WRITE(p, " float w[7]; w[0] = w1.x; w[1] = w1.y; w[2] = w1.z; w[3] = w1.w; w[4] = w2.x; w[5] = w2.y; w[6] = w2.z;\n"); break;
|
||||
case 8: WRITE(p, " float w[8]; w[0] = w1.x; w[1] = w1.y; w[2] = w1.z; w[3] = w1.w; w[4] = w2.x; w[5] = w2.y; w[6] = w2.z; w[7] = w2.w;\n"); break;
|
||||
}
|
||||
|
||||
WRITE(p, " mat4 skinMatrix = w[0] * u_bone[0];\n");
|
||||
if (numBoneWeights > 1) {
|
||||
WRITE(p, " for (int i = 1; i < %i; i++) {\n", numBoneWeights);
|
||||
WRITE(p, " skinMatrix += w[i] * u_bone[i];\n");
|
||||
WRITE(p, " }\n");
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#ifdef USE_BONE_ARRAY
|
||||
if (numBoneWeights == 1)
|
||||
WRITE(p, " mat4 skinMatrix = w1 * u_bone[0]");
|
||||
WRITE(p, " vec3 worldpos = (u_world * vec4(position.xyz, 1.0)).xyz;\n");
|
||||
if (hasNormal)
|
||||
WRITE(p, " mediump vec3 worldnormal = normalize((u_world * vec4(%snormal, 0.0)).xyz);\n", flipNormal ? "-" : "");
|
||||
else
|
||||
WRITE(p, " mat4 skinMatrix = w1.x * u_bone[0]");
|
||||
for (int i = 1; i < numBoneWeights; i++) {
|
||||
const char *weightAttr = boneWeightAttr[i];
|
||||
// workaround for "cant do .x of scalar" issue
|
||||
if (numBoneWeights == 1 && i == 0) weightAttr = "w1";
|
||||
if (numBoneWeights == 5 && i == 4) weightAttr = "w2";
|
||||
WRITE(p, " + %s * u_bone[%i]", weightAttr, i);
|
||||
}
|
||||
#else
|
||||
// Uncomment this to screw up bone shaders to check the vertex shader software fallback
|
||||
// WRITE(p, "THIS SHOULD ERROR! #error");
|
||||
if (numBoneWeights == 1)
|
||||
WRITE(p, " mat4 skinMatrix = w1 * u_bone0");
|
||||
else
|
||||
WRITE(p, " mat4 skinMatrix = w1.x * u_bone0");
|
||||
for (int i = 1; i < numBoneWeights; i++) {
|
||||
const char *weightAttr = boneWeightAttr[i];
|
||||
// workaround for "cant do .x of scalar" issue
|
||||
if (numBoneWeights == 1 && i == 0) weightAttr = "w1";
|
||||
if (numBoneWeights == 5 && i == 4) weightAttr = "w2";
|
||||
WRITE(p, " + %s * u_bone%i", weightAttr, i);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
WRITE(p, ";\n");
|
||||
|
||||
// Trying to simplify this results in bugs in LBP...
|
||||
WRITE(p, " vec3 skinnedpos = (skinMatrix * vec4(position, 1.0)).xyz %s;\n", factor);
|
||||
WRITE(p, " vec3 worldpos = (u_world * vec4(skinnedpos, 1.0)).xyz;\n");
|
||||
|
||||
if (hasNormal) {
|
||||
WRITE(p, " mediump vec3 skinnednormal = (skinMatrix * vec4(%snormal, 0.0)).xyz %s;\n", flipNormal ? "-" : "", factor);
|
||||
} else {
|
||||
WRITE(p, " mediump vec3 skinnednormal = (skinMatrix * vec4(0.0, 0.0, %s1.0, 0.0)).xyz %s;\n", flipNormal ? "-" : "", factor);
|
||||
}
|
||||
WRITE(p, " mediump vec3 worldnormal = normalize((u_world * vec4(skinnednormal, 0.0)).xyz);\n");
|
||||
WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n");
|
||||
}
|
||||
|
||||
WRITE(p, " vec4 viewPos = u_view * vec4(worldpos, 1.0);\n");
|
||||
@@ -796,8 +664,8 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask,
|
||||
case GE_LIGHTTYPE_SPOT:
|
||||
case GE_LIGHTTYPE_UNKNOWN:
|
||||
WRITE(p, " angle = dot(normalize(u_lightdir%i), toLight);\n", i);
|
||||
WRITE(p, " if (angle >= u_lightangle%i) {\n", i);
|
||||
WRITE(p, " lightScale = clamp(1.0 / dot(u_lightatt%i, vec3(1.0, distance, distance*distance)), 0.0, 1.0) * pow(angle, u_lightspotCoef%i);\n", i, i);
|
||||
WRITE(p, " if (angle >= u_lightangle_spotCoef%i.x) {\n", i);
|
||||
WRITE(p, " lightScale = clamp(1.0 / dot(u_lightatt%i, vec3(1.0, distance, distance*distance)), 0.0, 1.0) * pow(angle, u_lightangle_spotCoef%i.y);\n", i, i);
|
||||
WRITE(p, " } else {\n");
|
||||
WRITE(p, " lightScale = 0.0;\n");
|
||||
WRITE(p, " }\n");
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
// #define USE_BONE_ARRAY
|
||||
|
||||
struct VShaderID;
|
||||
|
||||
void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask, uint64_t *uniformMask);
|
||||
+1
-1
@@ -110,6 +110,6 @@ bool GPU_Init(GraphicsContext *ctx, Draw::DrawContext *draw) {
|
||||
|
||||
void GPU_Shutdown() {
|
||||
delete gpu;
|
||||
gpu = 0;
|
||||
gpu = nullptr;
|
||||
gpuDebug = 0;
|
||||
}
|
||||
@@ -270,7 +270,9 @@
|
||||
<ClInclude Include="D3D11\StateMappingD3D11.h">
|
||||
<Filter>D3D11</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Vulkan\DebugVisVulkan.h" />
|
||||
<ClInclude Include="Vulkan\DebugVisVulkan.h">
|
||||
<Filter>Vulkan</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Math3D.cpp">
|
||||
@@ -537,6 +539,8 @@
|
||||
<ClCompile Include="Common\StencilCommon.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Vulkan\DebugVisVulkan.cpp" />
|
||||
<ClCompile Include="Vulkan\DebugVisVulkan.cpp">
|
||||
<Filter>Vulkan</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+303
-76
@@ -42,6 +42,15 @@ const CommonCommandTableEntry commonCommandTable[] = {
|
||||
{ GE_CMD_BJUMP, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, 0, &GPUCommon::Execute_BJump }, // EXECUTE
|
||||
{ GE_CMD_BOUNDINGBOX, FLAG_EXECUTE, 0, &GPUCommon::Execute_BoundingBox }, // + FLUSHBEFORE when we implement... or not, do we need to?
|
||||
|
||||
{ GE_CMD_PRIM, FLAG_EXECUTE, 0, &GPUCommon::Execute_Prim },
|
||||
{ GE_CMD_BEZIER, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPUCommon::Execute_Bezier },
|
||||
{ GE_CMD_SPLINE, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPUCommon::Execute_Spline },
|
||||
|
||||
// Changing the vertex type does not always require us to flush so handle that in Execute_VertexType.
|
||||
{ GE_CMD_VERTEXTYPE, FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_VertexType },
|
||||
|
||||
{ GE_CMD_LOADCLUT, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPUCommon::Execute_LoadClut },
|
||||
|
||||
// These two are actually processed in CMD_END. Not sure if FLAG_FLUSHBEFORE matters.
|
||||
{ GE_CMD_SIGNAL, FLAG_FLUSHBEFORE },
|
||||
{ GE_CMD_FINISH, FLAG_FLUSHBEFORE },
|
||||
@@ -121,7 +130,7 @@ const CommonCommandTableEntry commonCommandTable[] = {
|
||||
{ GE_CMD_TEXOFFSETU },
|
||||
{ GE_CMD_TEXOFFSETV },
|
||||
|
||||
// TEXSIZE0 is handled by each backend.
|
||||
{ GE_CMD_TEXSIZE0, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPUCommon::Execute_TexSize0 },
|
||||
{ GE_CMD_TEXSIZE1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },
|
||||
{ GE_CMD_TEXSIZE2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },
|
||||
{ GE_CMD_TEXSIZE3, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },
|
||||
@@ -343,6 +352,9 @@ const CommonCommandTableEntry commonCommandTable[] = {
|
||||
};
|
||||
size_t commonCommandTableSize = ARRAY_SIZE(commonCommandTable);
|
||||
|
||||
// TODO: Make class member?
|
||||
GPUCommon::CommandInfo GPUCommon::cmdInfo_[256];
|
||||
|
||||
void GPUCommon::Flush() {
|
||||
drawEngineCommon_->DispatchFlush();
|
||||
}
|
||||
@@ -366,6 +378,31 @@ GPUCommon::GPUCommon(GraphicsContext *gfxCtx, Draw::DrawContext *draw) :
|
||||
gstate.Reset();
|
||||
gstate_c.Reset();
|
||||
gpuStats.Reset();
|
||||
|
||||
memset(cmdInfo_, 0, sizeof(cmdInfo_));
|
||||
|
||||
// Convert the command table to a faster format, and check for dupes.
|
||||
std::set<u8> dupeCheck;
|
||||
for (size_t i = 0; i < commonCommandTableSize; i++) {
|
||||
const u8 cmd = commonCommandTable[i].cmd;
|
||||
if (dupeCheck.find(cmd) != dupeCheck.end()) {
|
||||
ERROR_LOG(G3D, "Command table Dupe: %02x (%i)", (int)cmd, (int)cmd);
|
||||
} else {
|
||||
dupeCheck.insert(cmd);
|
||||
}
|
||||
cmdInfo_[cmd].flags |= (uint64_t)commonCommandTable[i].flags | (commonCommandTable[i].dirty << 8);
|
||||
cmdInfo_[cmd].func = commonCommandTable[i].func;
|
||||
if ((cmdInfo_[cmd].flags & (FLAG_EXECUTE | FLAG_EXECUTEONCHANGE)) && !cmdInfo_[cmd].func) {
|
||||
// Can't have FLAG_EXECUTE commands without a function pointer to execute.
|
||||
Crash();
|
||||
}
|
||||
}
|
||||
// Find commands missing from the table.
|
||||
for (int i = 0; i < 0xEF; i++) {
|
||||
if (dupeCheck.find((u8)i) == dupeCheck.end()) {
|
||||
ERROR_LOG(G3D, "Command missing from table: %02x (%i)", i, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GPUCommon::~GPUCommon() {
|
||||
@@ -923,6 +960,46 @@ bool GPUCommon::InterpretList(DisplayList &list) {
|
||||
return gpuState == GPUSTATE_DONE || gpuState == GPUSTATE_ERROR;
|
||||
}
|
||||
|
||||
// Maybe should write this in ASM...
|
||||
void GPUCommon::FastRunLoop(DisplayList &list) {
|
||||
PROFILE_THIS_SCOPE("gpuloop");
|
||||
const CommandInfo *cmdInfo = cmdInfo_;
|
||||
int dc = downcount;
|
||||
for (; dc > 0; --dc) {
|
||||
// We know that display list PCs have the upper nibble == 0 - no need to mask the pointer
|
||||
const u32 op = *(const u32 *)(Memory::base + list.pc);
|
||||
const u32 cmd = op >> 24;
|
||||
const CommandInfo &info = cmdInfo[cmd];
|
||||
const u32 diff = op ^ gstate.cmdmem[cmd];
|
||||
if (diff == 0) {
|
||||
if (info.flags & FLAG_EXECUTE) {
|
||||
downcount = dc;
|
||||
(this->*info.func)(op, diff);
|
||||
dc = downcount;
|
||||
}
|
||||
} else {
|
||||
uint64_t flags = info.flags;
|
||||
if (flags & FLAG_FLUSHBEFOREONCHANGE) {
|
||||
if (drawEngineCommon_->GetNumDrawCalls()) {
|
||||
drawEngineCommon_->DispatchFlush();
|
||||
}
|
||||
}
|
||||
gstate.cmdmem[cmd] = op;
|
||||
if (flags & (FLAG_EXECUTE | FLAG_EXECUTEONCHANGE)) {
|
||||
downcount = dc;
|
||||
(this->*info.func)(op, diff);
|
||||
dc = downcount;
|
||||
} else {
|
||||
uint64_t dirty = flags >> 8;
|
||||
if (dirty)
|
||||
gstate_c.Dirty(dirty);
|
||||
}
|
||||
}
|
||||
list.pc += 4;
|
||||
}
|
||||
downcount = 0;
|
||||
}
|
||||
|
||||
void GPUCommon::BeginFrame() {
|
||||
immCount_ = 0;
|
||||
if (dumpNextFrame_) {
|
||||
@@ -980,7 +1057,6 @@ void GPUCommon::UpdatePC(u32 currentPC, u32 newPC) {
|
||||
}
|
||||
|
||||
// Exit the runloop and recalculate things. This happens a lot in some games.
|
||||
// No need to lock, this function is always called under listLock.
|
||||
if (currentList)
|
||||
downcount = currentList->stall == 0 ? 0x0FFFFFFF : (currentList->stall - newPC) / 4;
|
||||
else
|
||||
@@ -1321,8 +1397,9 @@ void GPUCommon::Execute_End(u32 op, u32 diff) {
|
||||
}
|
||||
|
||||
void GPUCommon::Execute_TexLevel(u32 op, u32 diff) {
|
||||
if (diff == 0xFFFFFFFF) return;
|
||||
|
||||
// TODO: If you change the rules here, don't forget to update the inner interpreter in Execute_Prim.
|
||||
if (diff == 0xFFFFFFFF)
|
||||
return;
|
||||
gstate.texlevel ^= diff;
|
||||
if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST && (0x00FF0000 & gstate.texlevel) != 0) {
|
||||
Flush();
|
||||
@@ -1343,37 +1420,216 @@ void GPUCommon::Execute_TexSize0(u32 op, u32 diff) {
|
||||
}
|
||||
}
|
||||
|
||||
void GPUCommon::Execute_VertexType(u32 op, u32 diff) {
|
||||
if (diff)
|
||||
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE);
|
||||
if (diff & (GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK)) {
|
||||
gstate_c.Dirty(DIRTY_UVSCALEOFFSET);
|
||||
if (diff & GE_VTYPE_THROUGH_MASK)
|
||||
gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE);
|
||||
}
|
||||
void GPUCommon::Execute_LoadClut(u32 op, u32 diff) {
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);
|
||||
textureCache_->LoadClut(gstate.getClutAddress(), gstate.getClutLoadBytes());
|
||||
}
|
||||
|
||||
void GPUCommon::Execute_VertexTypeSkinning(u32 op, u32 diff) {
|
||||
// Don't flush when weight count changes, unless morph is enabled.
|
||||
if ((diff & ~GE_VTYPE_WEIGHTCOUNT_MASK) || (op & GE_VTYPE_MORPHCOUNT_MASK) != 0) {
|
||||
void GPUCommon::Execute_VertexType(u32 op, u32 diff) {
|
||||
// Don't flush when weight count changes.
|
||||
if (diff & ~GE_VTYPE_WEIGHTCOUNT_MASK) {
|
||||
// Restore and flush
|
||||
gstate.vertType ^= diff;
|
||||
Flush();
|
||||
gstate.vertType ^= diff;
|
||||
if (diff & (GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK))
|
||||
gstate_c.Dirty(DIRTY_UVSCALEOFFSET);
|
||||
// In this case, we may be doing weights and morphs.
|
||||
// Update any bone matrix uniforms so it uses them correctly.
|
||||
if ((op & GE_VTYPE_MORPHCOUNT_MASK) != 0) {
|
||||
gstate_c.Dirty(gstate_c.deferredVertTypeDirty);
|
||||
gstate_c.deferredVertTypeDirty = 0;
|
||||
}
|
||||
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE);
|
||||
}
|
||||
if (diff & GE_VTYPE_THROUGH_MASK)
|
||||
gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE);
|
||||
}
|
||||
|
||||
|
||||
void GPUCommon::Execute_Prim(u32 op, u32 diff) {
|
||||
// This drives all drawing. All other state we just buffer up, then we apply it only
|
||||
// when it's time to draw. As most PSP games set state redundantly ALL THE TIME, this is a huge optimization.
|
||||
|
||||
PROFILE_THIS_SCOPE("execprim");
|
||||
|
||||
u32 data = op & 0xFFFFFF;
|
||||
u32 count = data & 0xFFFF;
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
// Upper bits are ignored.
|
||||
GEPrimitiveType prim = static_cast<GEPrimitiveType>((data >> 16) & 7);
|
||||
SetDrawType(DRAW_PRIM, prim);
|
||||
|
||||
// Discard AA lines as we can't do anything that makes sense with these anyway. The SW plugin might, though.
|
||||
if (gstate.isAntiAliasEnabled()) {
|
||||
// Discard AA lines in DOA
|
||||
if (prim == GE_PRIM_LINE_STRIP)
|
||||
return;
|
||||
// Discard AA lines in Summon Night 5
|
||||
if ((prim == GE_PRIM_LINES) && gstate.isSkinningEnabled())
|
||||
return;
|
||||
}
|
||||
|
||||
// This also makes skipping drawing very effective.
|
||||
framebufferManager_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason);
|
||||
|
||||
if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) {
|
||||
// Rough estimate, not sure what's correct.
|
||||
cyclesExecuted += EstimatePerVertexCost() * count;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Memory::IsValidAddress(gstate_c.vertexAddr)) {
|
||||
ERROR_LOG_REPORT(G3D, "Bad vertex address %08x!", gstate_c.vertexAddr);
|
||||
return;
|
||||
}
|
||||
|
||||
void *verts = Memory::GetPointerUnchecked(gstate_c.vertexAddr);
|
||||
void *inds = 0;
|
||||
u32 vertexType = gstate.vertType;
|
||||
if ((vertexType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
|
||||
u32 indexAddr = gstate_c.indexAddr;
|
||||
if (!Memory::IsValidAddress(indexAddr)) {
|
||||
ERROR_LOG_REPORT(G3D, "Bad index address %08x!", indexAddr);
|
||||
return;
|
||||
}
|
||||
inds = Memory::GetPointerUnchecked(indexAddr);
|
||||
}
|
||||
|
||||
#ifndef MOBILE_DEVICE
|
||||
if (prim > GE_PRIM_RECTANGLES) {
|
||||
ERROR_LOG_REPORT_ONCE(reportPrim, G3D, "Unexpected prim type: %d", prim);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gstate_c.dirty & DIRTY_VERTEXSHADER_STATE) {
|
||||
vertexCost_ = EstimatePerVertexCost();
|
||||
}
|
||||
|
||||
int bytesRead = 0;
|
||||
UpdateUVScaleOffset();
|
||||
|
||||
uint32_t vertTypeID = GetVertTypeID(vertexType, gstate.getUVGenMode());
|
||||
drawEngineCommon_->SubmitPrim(verts, inds, prim, count, vertTypeID, &bytesRead);
|
||||
// After drawing, we advance the vertexAddr (when non indexed) or indexAddr (when indexed).
|
||||
// Some games rely on this, they don't bother reloading VADDR and IADDR.
|
||||
// The VADDR/IADDR registers are NOT updated.
|
||||
AdvanceVerts(vertexType, count, bytesRead);
|
||||
int totalVertCount = count;
|
||||
|
||||
// PRIMs are often followed by more PRIMs. Save some work and submit them immediately.
|
||||
const u32 *src = (const u32 *)Memory::GetPointerUnchecked(currentList->pc + 4);
|
||||
const u32 *stall = currentList->stall ? (const u32 *)Memory::GetPointerUnchecked(currentList->stall) : 0;
|
||||
int cmdCount = 0;
|
||||
|
||||
// Optimized submission of sequences of PRIM. Allows us to avoid going through all the mess
|
||||
// above for each one. This can be expanded to support additional games that intersperse
|
||||
// PRIM commands with other commands. A special case that might be interesting is that game
|
||||
// that changes culling mode between each prim, we could just change the triangle winding
|
||||
// right here to still be able to join draw calls.
|
||||
#ifndef MOBILE_DEVICE
|
||||
if (debugRecording_ || host->GPUDebuggingActive())
|
||||
goto bail;
|
||||
#endif
|
||||
|
||||
while (src != stall) {
|
||||
uint32_t data = *src;
|
||||
switch (data >> 24) {
|
||||
case GE_CMD_PRIM:
|
||||
{
|
||||
u32 count = data & 0xFFFF;
|
||||
if (count == 0) {
|
||||
// Ignore.
|
||||
break;
|
||||
}
|
||||
|
||||
GEPrimitiveType newPrim = static_cast<GEPrimitiveType>((data >> 16) & 7);
|
||||
// TODO: more efficient updating of verts/inds
|
||||
verts = Memory::GetPointerUnchecked(gstate_c.vertexAddr);
|
||||
inds = 0;
|
||||
if ((vertexType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
|
||||
inds = Memory::GetPointerUnchecked(gstate_c.indexAddr);
|
||||
}
|
||||
|
||||
drawEngineCommon_->SubmitPrim(verts, inds, newPrim, count, vertTypeID, &bytesRead);
|
||||
AdvanceVerts(vertexType, count, bytesRead);
|
||||
totalVertCount += count;
|
||||
break;
|
||||
}
|
||||
case GE_CMD_VERTEXTYPE:
|
||||
{
|
||||
uint32_t diff = data ^ vertexType;
|
||||
// don't mask upper bits, vertexType is unmasked
|
||||
if (diff & ~GE_VTYPE_WEIGHTCOUNT_MASK) {
|
||||
goto bail;
|
||||
} else {
|
||||
vertexType = data;
|
||||
vertTypeID = GetVertTypeID(vertexType, gstate.getUVGenMode());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GE_CMD_VADDR:
|
||||
gstate_c.vertexAddr = gstate_c.getRelativeAddress(data & 0x00FFFFFF);
|
||||
break;
|
||||
case GE_CMD_OFFSETADDR:
|
||||
gstate.cmdmem[GE_CMD_OFFSETADDR] = data;
|
||||
gstate_c.offsetAddr = data << 8;
|
||||
break;
|
||||
case GE_CMD_BASE:
|
||||
gstate.cmdmem[GE_CMD_BASE] = data;
|
||||
break;
|
||||
case GE_CMD_NOP:
|
||||
case GE_CMD_NOP_FF:
|
||||
break;
|
||||
case GE_CMD_BONEMATRIXNUMBER:
|
||||
gstate.cmdmem[GE_CMD_BONEMATRIXNUMBER] = data;
|
||||
break;
|
||||
case GE_CMD_TEXSCALEU:
|
||||
gstate.cmdmem[GE_CMD_TEXSCALEU] = data;
|
||||
gstate_c.uv.uScale = getFloat24(data);
|
||||
break;
|
||||
case GE_CMD_TEXSCALEV:
|
||||
gstate.cmdmem[GE_CMD_TEXSCALEV] = data;
|
||||
gstate_c.uv.vScale = getFloat24(data);
|
||||
break;
|
||||
case GE_CMD_TEXLEVEL:
|
||||
// Same Gran Turismo hack from Execute_TexLevel
|
||||
if ((data & 3) != GE_TEXLEVEL_MODE_AUTO && (0x00FF0000 & data) != 0) {
|
||||
goto bail;
|
||||
}
|
||||
gstate.cmdmem[GE_CMD_TEXLEVEL] = data;
|
||||
break;
|
||||
case GE_CMD_CALL:
|
||||
{
|
||||
// A bone matrix probably. If not we bail.
|
||||
const u32 target = gstate_c.getRelativeAddress(data & 0x00FFFFFC);
|
||||
if ((Memory::ReadUnchecked_U32(target) >> 24) == GE_CMD_BONEMATRIXDATA &&
|
||||
(Memory::ReadUnchecked_U32(target + 11 * 4) >> 24) == GE_CMD_BONEMATRIXDATA &&
|
||||
(Memory::ReadUnchecked_U32(target + 12 * 4) >> 24) == GE_CMD_RET &&
|
||||
(target > currentList->stall || target + 12 * 4 < currentList->stall)) {
|
||||
FastLoadBoneMatrix(target);
|
||||
} else {
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// All other commands might need a flush or something, stop this inner loop.
|
||||
goto bail;
|
||||
}
|
||||
cmdCount++;
|
||||
src++;
|
||||
}
|
||||
|
||||
bail:
|
||||
gstate.cmdmem[GE_CMD_VERTEXTYPE] = vertexType;
|
||||
// Skip over the commands we just read out manually.
|
||||
if (cmdCount > 0) {
|
||||
UpdatePC(currentList->pc, currentList->pc + cmdCount * 4);
|
||||
currentList->pc += cmdCount * 4;
|
||||
}
|
||||
|
||||
gpuStats.vertexGPUCycles += vertexCost_ * totalVertCount;
|
||||
cyclesExecuted += vertexCost_ * totalVertCount;
|
||||
}
|
||||
|
||||
void GPUCommon::Execute_Bezier(u32 op, u32 diff) {
|
||||
drawEngineCommon_->DispatchFlush();
|
||||
|
||||
@@ -1402,10 +1658,6 @@ void GPUCommon::Execute_Bezier(u32 op, u32 diff) {
|
||||
indices = Memory::GetPointerUnchecked(gstate_c.indexAddr);
|
||||
}
|
||||
|
||||
if ((gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) || vertTypeIsSkinningEnabled(gstate.vertType)) {
|
||||
DEBUG_LOG_REPORT(G3D, "Unusual bezier/spline vtype: %08x, morph: %d, bones: %d", gstate.vertType, (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT, vertTypeGetNumBoneWeights(gstate.vertType));
|
||||
}
|
||||
|
||||
GEPatchPrimType patchPrim = gstate.getPatchPrimitiveType();
|
||||
SetDrawType(DRAW_BEZIER, PatchPrimToPrim(patchPrim));
|
||||
|
||||
@@ -1464,10 +1716,6 @@ void GPUCommon::Execute_Spline(u32 op, u32 diff) {
|
||||
indices = Memory::GetPointerUnchecked(gstate_c.indexAddr);
|
||||
}
|
||||
|
||||
if ((gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) || vertTypeIsSkinningEnabled(gstate.vertType)) {
|
||||
DEBUG_LOG_REPORT(G3D, "Unusual bezier/spline vtype: %08x, morph: %d, bones: %d", gstate.vertType, (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT, vertTypeGetNumBoneWeights(gstate.vertType));
|
||||
}
|
||||
|
||||
int sp_ucount = op & 0xFF;
|
||||
int sp_vcount = (op >> 8) & 0xFF;
|
||||
int sp_utype = (op >> 16) & 0x3;
|
||||
@@ -1748,34 +1996,10 @@ void GPUCommon::Execute_BoneMtxNum(u32 op, u32 diff) {
|
||||
}
|
||||
|
||||
if (fastLoad) {
|
||||
// If we can't use software skinning, we have to flush and dirty.
|
||||
if (!g_Config.bSoftwareSkinning || (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) != 0) {
|
||||
while ((src[i] >> 24) == GE_CMD_BONEMATRIXDATA) {
|
||||
const u32 newVal = src[i] << 8;
|
||||
if (dst[i] != newVal) {
|
||||
Flush();
|
||||
dst[i] = newVal;
|
||||
}
|
||||
if (++i >= end) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned int numPlusCount = (op & 0x7F) + i;
|
||||
for (unsigned int num = op & 0x7F; num < numPlusCount; num += 12) {
|
||||
gstate_c.Dirty(DIRTY_BONEMATRIX0 << (num / 12));
|
||||
}
|
||||
} else {
|
||||
while ((src[i] >> 24) == GE_CMD_BONEMATRIXDATA) {
|
||||
dst[i] = src[i] << 8;
|
||||
if (++i >= end) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned int numPlusCount = (op & 0x7F) + i;
|
||||
for (unsigned int num = op & 0x7F; num < numPlusCount; num += 12) {
|
||||
gstate_c.deferredVertTypeDirty |= DIRTY_BONEMATRIX0 << (num / 12);
|
||||
while ((src[i] >> 24) == GE_CMD_BONEMATRIXDATA) {
|
||||
dst[i] = src[i] << 8;
|
||||
if (++i >= end) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1793,13 +2017,6 @@ void GPUCommon::Execute_BoneMtxData(u32 op, u32 diff) {
|
||||
int num = gstate.boneMatrixNumber & 0x7F;
|
||||
u32 newVal = op << 8;
|
||||
if (num < 96 && newVal != ((const u32 *)gstate.boneMatrix)[num]) {
|
||||
// Bone matrices should NOT flush when software skinning is enabled!
|
||||
if (!g_Config.bSoftwareSkinning || (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) != 0) {
|
||||
Flush();
|
||||
gstate_c.Dirty(DIRTY_BONEMATRIX0 << (num / 12));
|
||||
} else {
|
||||
gstate_c.deferredVertTypeDirty |= DIRTY_BONEMATRIX0 << (num / 12);
|
||||
}
|
||||
((u32 *)gstate.boneMatrix)[num] = newVal;
|
||||
}
|
||||
num++;
|
||||
@@ -1878,7 +2095,8 @@ void GPUCommon::FlushImm() {
|
||||
int vtype = GE_VTYPE_POS_FLOAT | GE_VTYPE_COL_8888 | GE_VTYPE_THROUGH;
|
||||
|
||||
int bytesRead;
|
||||
drawEngineCommon_->DispatchSubmitPrim(temp, nullptr, immPrim_, immCount_, vtype, &bytesRead);
|
||||
uint32_t vertTypeID = GetVertTypeID(vtype, 0);
|
||||
drawEngineCommon_->DispatchSubmitPrim(temp, nullptr, immPrim_, immCount_, vertTypeID, &bytesRead);
|
||||
drawEngineCommon_->DispatchFlush();
|
||||
// TOOD: In the future, make a special path for these.
|
||||
// drawEngineCommon_->DispatchSubmitImm(immBuffer_, immCount_);
|
||||
@@ -1939,17 +2157,6 @@ void GPUCommon::Execute_Unknown(u32 op, u32 diff) {
|
||||
void GPUCommon::FastLoadBoneMatrix(u32 target) {
|
||||
const int num = gstate.boneMatrixNumber & 0x7F;
|
||||
const int mtxNum = num / 12;
|
||||
uint32_t uniformsToDirty = DIRTY_BONEMATRIX0 << mtxNum;
|
||||
if ((num - 12 * mtxNum) != 0) {
|
||||
uniformsToDirty |= DIRTY_BONEMATRIX0 << ((mtxNum + 1) & 7);
|
||||
}
|
||||
|
||||
if (!g_Config.bSoftwareSkinning || (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) != 0) {
|
||||
Flush();
|
||||
gstate_c.Dirty(uniformsToDirty);
|
||||
} else {
|
||||
gstate_c.deferredVertTypeDirty |= uniformsToDirty;
|
||||
}
|
||||
gstate.FastLoadBoneMatrix(target);
|
||||
}
|
||||
|
||||
@@ -2442,3 +2649,23 @@ bool GPUCommon::DescribeCodePtr(const u8 *ptr, std::string &name) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GPUCommon::FramebufferDirty() {
|
||||
VirtualFramebuffer *vfb = framebufferManager_->GetDisplayVFB();
|
||||
if (vfb) {
|
||||
bool dirty = vfb->dirtyAfterDisplay;
|
||||
vfb->dirtyAfterDisplay = false;
|
||||
return dirty;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPUCommon::FramebufferReallyDirty() {
|
||||
VirtualFramebuffer *vfb = framebufferManager_->GetDisplayVFB();
|
||||
if (vfb) {
|
||||
bool dirty = vfb->reallyDirtyAfterDisplay;
|
||||
vfb->reallyDirtyAfterDisplay = false;
|
||||
return dirty;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Loaded 100 of 257 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user