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:
LunaMoo committed 2018-03-24 21:38:47 +01:00
commit fe2060bfe3
257 files changed
+22555 -19976

No files matched your search

+24 -5
View File
@@ -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
-2
View File
@@ -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.
-2
View File
@@ -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__)
+2 -1
View File
@@ -14,4 +14,5 @@
#undef min
#undef max
#endif
#undef DrawText
#endif
+2 -3
View File
@@ -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;
}
+9
View File
@@ -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;
};
+1 -1
View File
@@ -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);
+43 -32
View File
@@ -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_);
+11 -9
View File
@@ -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
View File
@@ -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, &copy_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, &copy_region);
vkCmdCopyBufferToImage(cmd, buffer, image_, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy_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
View File
@@ -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
View File
@@ -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() {
+1
View File
@@ -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);
+13 -17
View File
@@ -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
View File
@@ -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),
};
-1
View File
@@ -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
View File
@@ -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);
+4
View File
@@ -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
View File
@@ -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;
+10 -10
View File
@@ -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;
+1 -1
View File
@@ -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;
}
+1 -1
View File
@@ -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;
}
+3 -3
View File
@@ -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;
}
+2 -2
View File
@@ -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);
+3 -1
View File
@@ -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
+18 -6
View File
@@ -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, &param);
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();
+1
View File
@@ -78,6 +78,7 @@ StereoResampler::StereoResampler()
StereoResampler::~StereoResampler() {
delete[] m_buffer;
m_buffer = nullptr;
}
void StereoResampler::UpdateBufferSize() {
-10
View File
@@ -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();
+9 -2
View File
@@ -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";
+3 -1
View File
@@ -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);
+4 -3
View File
@@ -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
View File
@@ -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
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+74 -74
View File
@@ -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();
}
}
}
+24 -10
View File
@@ -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;
};
+27 -23
View File
@@ -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++;
}
+4 -4
View File
@@ -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
+5 -11
View File
@@ -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;
+2 -10
View File
@@ -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,
-12
View File
@@ -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.
+2 -7
View File
@@ -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
+2 -10
View File
@@ -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);
}
}
}
+8 -29
View File
@@ -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);
+7 -43
View File
@@ -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.
+5 -2
View File
@@ -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();
+26 -2
View File
@@ -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 {
+4 -11
View File
@@ -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);
}
+1 -53
View File
@@ -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 };
+1 -42
View File
@@ -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
View File
@@ -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];
+11 -72
View File
@@ -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
View File
@@ -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));
+2 -80
View File
@@ -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) {
-5
View File
@@ -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;
-18
View File
@@ -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() {
-4
View File
@@ -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
View File
@@ -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
View File
@@ -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_;
};
+6 -18
View File
@@ -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;
+1 -17
View File
@@ -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_;
+4
View File
@@ -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);
}
+8 -11
View File
@@ -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;
-3
View File
@@ -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;
+1 -2
View File
@@ -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);
}
+1 -1
View File
@@ -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);
+2 -76
View File
@@ -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) {
-5
View File
@@ -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();
+10 -19
View File
@@ -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() {
-1
View File
@@ -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
View File
@@ -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
View File
@@ -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
+3 -35
View File
@@ -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]);
-1
View File
@@ -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);
-7
View File
@@ -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();
-3
View File
@@ -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;
+106 -217
View File
@@ -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");
+22 -32
View File
@@ -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
};
+39 -115
View File
@@ -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;
}
+11 -8
View File
@@ -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
View File
@@ -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, &params, &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
View File
@@ -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 -30
View File
@@ -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;
+7 -8
View File
@@ -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
View File
@@ -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) {
+34 -44
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+15 -22
View File
@@ -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
View File
@@ -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();
}
}
+32 -36
View File
@@ -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
View File
@@ -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
View File
@@ -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 };
};
+107 -239
View File
@@ -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");
-2
View File
@@ -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
View File
@@ -110,6 +110,6 @@ bool GPU_Init(GraphicsContext *ctx, Draw::DrawContext *draw) {
void GPU_Shutdown() {
delete gpu;
gpu = 0;
gpu = nullptr;
gpuDebug = 0;
}
+6 -2
View File
@@ -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
View File
@@ -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