2020-03-31 18:55:57 +02:00
|
|
|
#include "EMULATOR.H"
|
|
|
|
|
|
|
|
#include "EMULATOR_VERSION.H"
|
|
|
|
#include "EMULATOR_GLOBALS.H"
|
|
|
|
#include "EMULATOR_PRIVATE.H"
|
|
|
|
#include "CRASHHANDLER.H"
|
|
|
|
|
|
|
|
#include "EMULATOR_PLATFORM_SETUP.H"
|
|
|
|
|
|
|
|
#include "LIBGPU.H"
|
|
|
|
#include "LIBETC.H"
|
|
|
|
#include "LIBPAD.H"
|
|
|
|
|
|
|
|
//#include <stdio.h>
|
|
|
|
//#include <string.h>
|
|
|
|
#if !defined(__ANDROID__)
|
|
|
|
//#include <thread>
|
|
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
#define SWAP_INTERVAL (2)
|
2020-03-31 18:55:57 +02:00
|
|
|
|
|
|
|
#if defined(NTSC_VERSION)
|
|
|
|
#define COUNTER_UPDATE_INTERVAL (263)
|
|
|
|
#else
|
|
|
|
#define COUNTER_UPDATE_INTERVAL (313)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2020-03-31 19:09:22 +02:00
|
|
|
#include <string.h>
|
2020-03-31 19:00:33 +02:00
|
|
|
#include <SDL.h>
|
2020-03-31 18:55:57 +02:00
|
|
|
|
|
|
|
SDL_Window* g_window = NULL;
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
#if defined(VK)
|
2020-03-31 18:55:57 +02:00
|
|
|
|
|
|
|
struct FrameBuffer vramFrameBuffer;
|
|
|
|
|
|
|
|
#define MAX_NUM_PHYSICAL_DEVICES (4)
|
|
|
|
|
|
|
|
VkWin32SurfaceCreateInfoKHR surfaceCreateInfo =
|
|
|
|
{
|
|
|
|
VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR
|
|
|
|
};
|
|
|
|
|
|
|
|
VkSurfaceKHR surface = VK_NULL_HANDLE;
|
|
|
|
VkInstance instance = VK_NULL_HANDLE;
|
|
|
|
VkPhysicalDeviceMemoryProperties deviceMemoryProperties;
|
|
|
|
|
|
|
|
const char* enabledExtensionsDeviceCreateInfo[] =
|
|
|
|
{
|
|
|
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME
|
|
|
|
};
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
MAX_DEVICE_COUNT = 8,
|
|
|
|
MAX_QUEUE_COUNT = 4,
|
|
|
|
MAX_PRESENT_MODE_COUNT = 6,
|
|
|
|
MAX_SWAPCHAIN_IMAGES = 3,
|
|
|
|
FRAME_COUNT = 2,
|
|
|
|
PRESENT_MODE_MAILBOX_IMAGE_COUNT = 3,
|
|
|
|
PRESENT_MODE_DEFAULT_IMAGE_COUNT = 2,
|
|
|
|
};
|
|
|
|
|
|
|
|
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
|
|
|
|
uint32_t queueFamilyIndex;
|
|
|
|
VkQueue queue;
|
|
|
|
VkDevice device = VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
unsigned int vramTexture;///@TODO trim me
|
|
|
|
unsigned int vramRenderBuffer = 0;
|
2020-03-31 19:09:22 +02:00
|
|
|
unsigned int whiteTexture;
|
2020-03-31 18:55:57 +02:00
|
|
|
int g_defaultFBO;
|
|
|
|
|
|
|
|
const char* enabledExtensions[] =
|
|
|
|
{
|
|
|
|
VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME
|
|
|
|
};
|
|
|
|
|
|
|
|
VkSwapchainKHR swapchain;
|
|
|
|
unsigned int swapchainImageCount;
|
|
|
|
VkImage swapchainImages[MAX_SWAPCHAIN_IMAGES];
|
|
|
|
VkExtent2D swapchainExtent;
|
|
|
|
VkSurfaceFormatKHR surfaceFormat;
|
|
|
|
unsigned int frameIndex = 0;
|
|
|
|
VkCommandPool commandPool;
|
|
|
|
VkCommandBuffer commandBuffers[FRAME_COUNT];
|
|
|
|
VkFence frameFences[FRAME_COUNT]; // Create with VK_FENCE_CREATE_SIGNALED_BIT.
|
|
|
|
VkSemaphore imageAvailableSemaphores[FRAME_COUNT];
|
|
|
|
VkSemaphore renderFinishedSemaphores[FRAME_COUNT];
|
|
|
|
#endif
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
TextureID vramTexture;
|
|
|
|
TextureID whiteTexture;
|
|
|
|
|
|
|
|
#if defined(OGLES) || defined(OGL)
|
|
|
|
GLuint dynamic_vertex_buffer;
|
|
|
|
GLuint dynamic_vertex_array;
|
|
|
|
#elif defined(D3D9)
|
|
|
|
IDirect3DVertexBuffer9 *dynamic_vertex_buffer = NULL;
|
|
|
|
IDirect3D9 *d3d;
|
|
|
|
IDirect3DDevice9 *d3ddev;
|
|
|
|
D3DPRESENT_PARAMETERS d3dpp;
|
|
|
|
#endif
|
|
|
|
|
2020-03-31 18:55:57 +02:00
|
|
|
int windowWidth = 0;
|
|
|
|
int windowHeight = 0;
|
|
|
|
char* pVirtualMemory = NULL;
|
|
|
|
SysCounter counters[3] = { 0 };
|
|
|
|
#if !defined(__ANDROID__)
|
|
|
|
//std::thread counter_thread;
|
|
|
|
#endif
|
2020-03-31 19:09:22 +02:00
|
|
|
|
|
|
|
int g_swapInterval = SWAP_INTERVAL;
|
|
|
|
int g_wireframeMode = 0;
|
|
|
|
int g_texturelessMode = 0;
|
|
|
|
TextureID g_lastBoundTexture;
|
|
|
|
|
|
|
|
void Emulator_ResetDevice()
|
|
|
|
{
|
|
|
|
#if defined(OGLES) || defined(OGL)
|
|
|
|
SDL_GL_SetSwapInterval(g_swapInterval);
|
|
|
|
#elif defined(D3D9)
|
|
|
|
if (dynamic_vertex_buffer) {
|
|
|
|
dynamic_vertex_buffer->Release();
|
|
|
|
dynamic_vertex_buffer = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
d3dpp.PresentationInterval = g_swapInterval ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;
|
|
|
|
d3dpp.BackBufferWidth = windowWidth;
|
|
|
|
d3dpp.BackBufferHeight = windowHeight;
|
|
|
|
HRESULT hr = d3ddev->Reset(&d3dpp);
|
|
|
|
assert(!FAILED(hr));
|
|
|
|
|
|
|
|
hr = d3ddev->CreateVertexBuffer(sizeof(Vertex) * MAX_NUM_POLY_BUFFER_VERTICES, D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT, &dynamic_vertex_buffer, NULL);
|
|
|
|
assert(!FAILED(hr));
|
|
|
|
#endif
|
|
|
|
}
|
2020-03-31 18:55:57 +02:00
|
|
|
|
|
|
|
#if defined(D3D9)
|
|
|
|
static int Emulator_InitialiseD3D9Context(char* windowName)
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
g_window = SDL_CreateWindow(windowName, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, windowWidth, windowHeight, SDL_WINDOW_RESIZABLE);
|
2020-03-31 18:55:57 +02:00
|
|
|
if (g_window == NULL)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to initialise SDL window!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
2020-03-31 19:09:22 +02:00
|
|
|
|
|
|
|
SDL_SysWMinfo wmInfo;
|
|
|
|
SDL_VERSION(&wmInfo.version);
|
|
|
|
SDL_GetWindowWMInfo(g_window, &wmInfo);
|
|
|
|
|
|
|
|
memset(&d3dpp, 0, sizeof(d3dpp));
|
|
|
|
d3dpp.Windowed = TRUE;
|
|
|
|
d3dpp.BackBufferCount = 1;
|
|
|
|
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
|
|
|
|
d3dpp.BackBufferWidth = windowWidth;
|
|
|
|
d3dpp.BackBufferHeight = windowHeight;
|
|
|
|
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
|
|
|
d3dpp.hDeviceWindow = wmInfo.info.win.window;
|
|
|
|
d3dpp.EnableAutoDepthStencil = TRUE;
|
|
|
|
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
|
|
|
|
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
|
|
|
|
|
|
|
|
d3d = Direct3DCreate9(D3D_SDK_VERSION);
|
|
|
|
if (!d3d) {
|
|
|
|
eprinterr("Failed to initialise D3D\n");
|
2020-03-31 18:55:57 +02:00
|
|
|
return FALSE;
|
|
|
|
}
|
2020-03-31 19:09:22 +02:00
|
|
|
|
|
|
|
HRESULT hr = d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, d3dpp.hDeviceWindow, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &d3ddev);
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
eprinterr("Failed to obtain D3D9 device!\n");
|
2020-03-31 18:55:57 +02:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(VK)
|
|
|
|
static int Emulator_InitialiseVKContext(char* windowName)
|
|
|
|
{
|
|
|
|
VkApplicationInfo appInfo;
|
|
|
|
memset(&appInfo, 0, sizeof(VkApplicationInfo));
|
|
|
|
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
|
|
|
appInfo.pApplicationName = windowName;
|
|
|
|
appInfo.applicationVersion = VK_MAKE_VERSION(EMULATOR_MAJOR_VERSION, EMULATOR_MINOR_VERSION, 0);
|
|
|
|
appInfo.pEngineName = EMULATOR_NAME;
|
|
|
|
appInfo.engineVersion = VK_MAKE_VERSION(EMULATOR_MAJOR_VERSION, EMULATOR_MINOR_VERSION, 0);
|
|
|
|
appInfo.apiVersion = VK_API_VERSION_1_0;
|
|
|
|
|
|
|
|
VkInstanceCreateInfo createInfo;
|
|
|
|
memset(&createInfo, 0, sizeof(VkInstanceCreateInfo));
|
|
|
|
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
|
|
|
createInfo.pApplicationInfo = &appInfo;
|
|
|
|
createInfo.enabledExtensionCount = 2;
|
|
|
|
createInfo.ppEnabledExtensionNames = enabledExtensions;
|
|
|
|
createInfo.pNext = VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
//Create Vulkan Instance
|
|
|
|
if (vkCreateInstance(&createInfo, NULL, &instance) != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to create Vulkan instance!");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_window = SDL_CreateWindow(windowName, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, windowWidth, windowHeight, SDL_WINDOW_VULKAN);
|
|
|
|
|
|
|
|
#if defined(OGL)
|
|
|
|
SDL_GL_CreateContext(g_window);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (g_window == NULL)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to initialise Vulkan context!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_SysWMinfo sysInfo;
|
|
|
|
SDL_VERSION(&sysInfo.version);
|
|
|
|
SDL_GetWindowWMInfo(g_window, &sysInfo);
|
|
|
|
surfaceCreateInfo.hinstance = GetModuleHandle(0);
|
|
|
|
surfaceCreateInfo.hwnd = sysInfo.info.win.window;
|
|
|
|
|
|
|
|
if (vkCreateWin32SurfaceKHR(instance, &surfaceCreateInfo, NULL, &surface) != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to initialise Vulkan surface!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int physicalDeviceCount;
|
|
|
|
VkPhysicalDevice deviceHandles[MAX_DEVICE_COUNT];
|
|
|
|
VkQueueFamilyProperties queueFamilyProperties[MAX_QUEUE_COUNT];
|
|
|
|
VkPhysicalDeviceProperties deviceProperties;
|
|
|
|
VkPhysicalDeviceFeatures deviceFeatures;
|
|
|
|
|
|
|
|
|
|
|
|
vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, 0);
|
|
|
|
physicalDeviceCount = physicalDeviceCount > MAX_DEVICE_COUNT ? MAX_DEVICE_COUNT : physicalDeviceCount;
|
|
|
|
vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, deviceHandles);
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < physicalDeviceCount; ++i)//Maybe 0 needs to be 1
|
|
|
|
{
|
|
|
|
unsigned int queueFamilyCount = 0;
|
|
|
|
vkGetPhysicalDeviceQueueFamilyProperties(deviceHandles[i], &queueFamilyCount, NULL);
|
|
|
|
queueFamilyCount = queueFamilyCount > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : queueFamilyCount;
|
|
|
|
vkGetPhysicalDeviceQueueFamilyProperties(deviceHandles[i], &queueFamilyCount, queueFamilyProperties);
|
|
|
|
|
|
|
|
vkGetPhysicalDeviceProperties(deviceHandles[i], &deviceProperties);
|
|
|
|
vkGetPhysicalDeviceFeatures(deviceHandles[i], &deviceFeatures);
|
|
|
|
vkGetPhysicalDeviceMemoryProperties(deviceHandles[i], &deviceMemoryProperties);
|
|
|
|
for (unsigned int j = 0; j < queueFamilyCount; ++j) {
|
|
|
|
|
|
|
|
VkBool32 supportsPresent = VK_FALSE;
|
|
|
|
vkGetPhysicalDeviceSurfaceSupportKHR(deviceHandles[i], j, surface, &supportsPresent);
|
|
|
|
|
|
|
|
if (supportsPresent && (queueFamilyProperties[j].queueFlags & VK_QUEUE_GRAPHICS_BIT))
|
|
|
|
{
|
|
|
|
queueFamilyIndex = j;
|
|
|
|
physicalDevice = deviceHandles[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (physicalDevice)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VkDeviceCreateInfo deviceCreateInfo;
|
|
|
|
VkDeviceQueueCreateInfo deviceQueueCreateInfo;
|
|
|
|
memset(&deviceCreateInfo, 0, sizeof(VkDeviceCreateInfo));
|
|
|
|
memset(&deviceQueueCreateInfo, 0, sizeof(VkDeviceQueueCreateInfo));
|
|
|
|
|
|
|
|
const float queuePriorities = { 1.0f };
|
|
|
|
|
|
|
|
deviceQueueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
|
|
deviceQueueCreateInfo.queueFamilyIndex = queueFamilyIndex;
|
|
|
|
deviceQueueCreateInfo.queueCount = 1;
|
|
|
|
deviceQueueCreateInfo.pQueuePriorities = &queuePriorities;
|
|
|
|
|
|
|
|
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
|
|
|
deviceCreateInfo.queueCreateInfoCount = 1;
|
|
|
|
deviceCreateInfo.pQueueCreateInfos = 0;
|
|
|
|
deviceCreateInfo.enabledLayerCount = 0;
|
|
|
|
deviceCreateInfo.ppEnabledLayerNames = 0;
|
|
|
|
deviceCreateInfo.enabledExtensionCount = 1;
|
|
|
|
deviceCreateInfo.ppEnabledExtensionNames = enabledExtensionsDeviceCreateInfo;
|
|
|
|
deviceCreateInfo.pEnabledFeatures = 0;
|
|
|
|
deviceCreateInfo.pQueueCreateInfos = &deviceQueueCreateInfo;
|
|
|
|
|
|
|
|
if (vkCreateDevice(physicalDevice, &deviceCreateInfo, NULL, &device) != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to create VK device!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
vkGetDeviceQueue(device, queueFamilyIndex, 0, &queue);
|
|
|
|
|
|
|
|
/* Initialise SwapChain */
|
|
|
|
unsigned int formatCount = 1;
|
|
|
|
vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, 0);
|
|
|
|
vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, &surfaceFormat);
|
|
|
|
surfaceFormat.format = surfaceFormat.format == VK_FORMAT_UNDEFINED ? VK_FORMAT_B8G8R8A8_UNORM : surfaceFormat.format;
|
|
|
|
|
|
|
|
unsigned int presentModeCount = 0;
|
|
|
|
vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, NULL);
|
|
|
|
VkPresentModeKHR presentModes[MAX_PRESENT_MODE_COUNT];
|
|
|
|
presentModeCount = presentModeCount > MAX_PRESENT_MODE_COUNT ? MAX_PRESENT_MODE_COUNT : presentModeCount;
|
|
|
|
vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, presentModes);
|
|
|
|
|
|
|
|
VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
|
|
|
|
for (unsigned int i = 0; i < presentModeCount; ++i)
|
|
|
|
{
|
|
|
|
if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
|
|
|
|
{
|
|
|
|
presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
swapchainImageCount = presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? PRESENT_MODE_MAILBOX_IMAGE_COUNT : PRESENT_MODE_DEFAULT_IMAGE_COUNT;
|
|
|
|
|
|
|
|
VkSurfaceCapabilitiesKHR surfaceCapabilities;
|
|
|
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfaceCapabilities);
|
|
|
|
|
|
|
|
swapchainExtent = surfaceCapabilities.currentExtent;
|
|
|
|
//if (swapchainExtent.width == UINT32_MAX)
|
|
|
|
//{
|
|
|
|
// swapchainExtent.width = clamp_u32(width, surfaceCapabilities.minImageExtent.width, surfaceCapabilities.maxImageExtent.width);
|
|
|
|
// swapchainExtent.height = clamp_u32(height, surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.height);
|
|
|
|
//}
|
|
|
|
|
|
|
|
VkSwapchainCreateInfoKHR swapChainCreateInfo;
|
|
|
|
memset(&swapChainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR));
|
|
|
|
swapChainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
|
|
|
swapChainCreateInfo.surface = surface;
|
|
|
|
swapChainCreateInfo.minImageCount = swapchainImageCount;
|
|
|
|
swapChainCreateInfo.imageFormat = surfaceFormat.format;
|
|
|
|
swapChainCreateInfo.imageColorSpace = surfaceFormat.colorSpace;
|
|
|
|
swapChainCreateInfo.imageExtent = swapchainExtent;
|
|
|
|
swapChainCreateInfo.imageArrayLayers = 1; // 2 for stereo
|
|
|
|
swapChainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
|
|
swapChainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
|
|
swapChainCreateInfo.preTransform = surfaceCapabilities.currentTransform;
|
|
|
|
swapChainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
|
|
|
swapChainCreateInfo.presentMode = presentMode;
|
|
|
|
swapChainCreateInfo.clipped = VK_TRUE;
|
|
|
|
|
|
|
|
if (vkCreateSwapchainKHR(device, &swapChainCreateInfo, 0, &swapchain) != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to create swap chain!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, NULL);
|
|
|
|
vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, swapchainImages);
|
|
|
|
|
|
|
|
|
|
|
|
VkCommandPoolCreateInfo commandPoolCreateInfo;
|
|
|
|
memset(&commandPoolCreateInfo, 0, sizeof(VkCommandPoolCreateInfo));
|
|
|
|
commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
|
|
commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
|
|
commandPoolCreateInfo.queueFamilyIndex = queueFamilyIndex;
|
|
|
|
|
|
|
|
vkCreateCommandPool(device, &commandPoolCreateInfo, 0, &commandPool);
|
|
|
|
|
|
|
|
VkCommandBufferAllocateInfo commandBufferAllocInfo;
|
|
|
|
memset(&commandBufferAllocInfo, 0, sizeof(VkCommandBufferAllocateInfo));
|
|
|
|
|
|
|
|
commandBufferAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
|
|
commandBufferAllocInfo.commandPool = commandPool;
|
|
|
|
commandBufferAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
|
|
commandBufferAllocInfo.commandBufferCount = FRAME_COUNT;
|
|
|
|
|
|
|
|
vkAllocateCommandBuffers(device, &commandBufferAllocInfo, commandBuffers);
|
|
|
|
|
|
|
|
VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
|
|
|
|
|
|
|
|
vkCreateSemaphore(device, &semaphoreCreateInfo, 0, &imageAvailableSemaphores[0]);
|
|
|
|
vkCreateSemaphore(device, &semaphoreCreateInfo, 0, &imageAvailableSemaphores[1]);
|
|
|
|
vkCreateSemaphore(device, &semaphoreCreateInfo, 0, &renderFinishedSemaphores[0]);
|
|
|
|
vkCreateSemaphore(device, &semaphoreCreateInfo, 0, &renderFinishedSemaphores[1]);
|
|
|
|
|
|
|
|
VkFenceCreateInfo fenceCreateInfo;
|
|
|
|
memset(&fenceCreateInfo, 0, sizeof(VkFenceCreateInfo));
|
|
|
|
fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
|
|
fenceCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
|
|
|
|
|
|
|
vkCreateFence(device, &fenceCreateInfo, 0, &frameFences[0]);
|
|
|
|
vkCreateFence(device, &fenceCreateInfo, 0, &frameFences[1]);
|
|
|
|
|
|
|
|
uint32_t index = (frameIndex++) % FRAME_COUNT;
|
|
|
|
vkWaitForFences(device, 1, &frameFences[index], VK_TRUE, UINT64_MAX);
|
|
|
|
vkResetFences(device, 1, &frameFences[index]);
|
|
|
|
|
|
|
|
uint32_t imageIndex;
|
|
|
|
vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, imageAvailableSemaphores[index], VK_NULL_HANDLE, &imageIndex);
|
|
|
|
|
|
|
|
VkCommandBufferBeginInfo beginInfo;
|
|
|
|
memset(&beginInfo, 0, sizeof(VkCommandBufferBeginInfo));
|
|
|
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
|
|
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
|
|
|
vkBeginCommandBuffer(commandBuffers[index], &beginInfo);
|
|
|
|
|
|
|
|
vkEndCommandBuffer(commandBuffers[index]);
|
|
|
|
|
|
|
|
VkSubmitInfo submitInfo;
|
|
|
|
memset(&submitInfo, 0, sizeof(VkSubmitInfo));
|
|
|
|
VkPipelineStageFlags writeDestStageMask = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
|
|
|
|
|
|
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
|
|
submitInfo.waitSemaphoreCount = 1;
|
|
|
|
submitInfo.pWaitSemaphores = &imageAvailableSemaphores[index];
|
|
|
|
submitInfo.pWaitDstStageMask = &writeDestStageMask;
|
|
|
|
submitInfo.commandBufferCount = 1;
|
|
|
|
submitInfo.pCommandBuffers = &commandBuffers[index];
|
|
|
|
submitInfo.signalSemaphoreCount = 1;
|
|
|
|
submitInfo.pSignalSemaphores = &renderFinishedSemaphores[index];
|
|
|
|
vkQueueSubmit(queue, 1, &submitInfo, frameFences[index]);
|
|
|
|
|
|
|
|
VkPresentInfoKHR presentInfo;
|
|
|
|
memset(&presentInfo, 0, sizeof(VkPresentInfoKHR));
|
|
|
|
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
|
|
|
presentInfo.waitSemaphoreCount = 1;
|
|
|
|
presentInfo.pWaitSemaphores = &renderFinishedSemaphores[index];
|
|
|
|
presentInfo.swapchainCount = 1;
|
|
|
|
presentInfo.pSwapchains = &swapchain;
|
|
|
|
presentInfo.pImageIndices = &imageIndex;
|
|
|
|
|
|
|
|
vkQueuePresentKHR(queue, &presentInfo);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int Emulator_InitialiseGLContext(char* windowName)
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
g_window = SDL_CreateWindow(windowName, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, windowWidth, windowHeight, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
|
2020-03-31 18:55:57 +02:00
|
|
|
|
|
|
|
#if defined(OGL)
|
|
|
|
SDL_GL_CreateContext(g_window);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (g_window == NULL)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to initialise SDL window or GL context!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(OGLES)
|
|
|
|
EGLint majorVersion = 0, minorVersion = 0;
|
|
|
|
EGLContext eglContext = NULL;
|
|
|
|
EGLSurface eglSurface = NULL;
|
|
|
|
EGLConfig eglConfig = NULL;
|
|
|
|
EGLDisplay eglDisplay = NULL;
|
|
|
|
int numConfigs = 0;
|
|
|
|
|
|
|
|
const EGLint config16bpp[] =
|
|
|
|
{
|
|
|
|
#if OGLES_VERSION == 2
|
|
|
|
EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT,
|
|
|
|
#elif OGLES_VERSION == 3
|
|
|
|
EGL_RENDERABLE_TYPE,EGL_OPENGL_ES3_BIT,
|
|
|
|
#endif
|
|
|
|
EGL_BUFFER_SIZE,24,
|
|
|
|
EGL_RED_SIZE,8,
|
|
|
|
EGL_GREEN_SIZE,8,
|
|
|
|
EGL_BLUE_SIZE,8,
|
|
|
|
EGL_ALPHA_SIZE,0,
|
|
|
|
EGL_DEPTH_SIZE,24,
|
|
|
|
EGL_STENCIL_SIZE,0,
|
|
|
|
EGL_SAMPLE_BUFFERS,1,
|
|
|
|
EGL_SAMPLES,4,
|
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
|
|
|
|
static int Emulator_InitialiseGLESContext(char* windowName)
|
|
|
|
{
|
|
|
|
unsigned int windowFlags = SDL_WINDOW_OPENGL;
|
|
|
|
|
|
|
|
#if defined(__ANDROID__)
|
|
|
|
windowFlags |= SDL_WINDOW_FULLSCREEN;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
|
|
|
g_window = SDL_CreateWindow(windowName, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, windowWidth, windowHeight, windowFlags);
|
|
|
|
|
|
|
|
if(g_window == NULL)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to create SDL window!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!eglInitialize(eglDisplay, &majorVersion, &minorVersion))
|
|
|
|
{
|
|
|
|
eprinterr("eglInitialize failure! Error: %x\n", eglGetError());
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
eglBindAPI(EGL_OPENGL_ES_API);
|
|
|
|
|
|
|
|
if (!eglChooseConfig(eglDisplay, config16bpp, &eglConfig, 1, &numConfigs))
|
|
|
|
{
|
|
|
|
printf("eglChooseConfig failed\n");
|
|
|
|
if (eglContext == 0)
|
|
|
|
{
|
|
|
|
printf("Error code: %d\n", eglGetError());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_SysWMinfo systemInfo;
|
|
|
|
SDL_VERSION(&systemInfo.version);
|
|
|
|
SDL_GetWindowWMInfo(g_window, &systemInfo);
|
|
|
|
#if defined(__EMSCRIPTEN__)
|
|
|
|
EGLNativeWindowType dummyWindow;
|
|
|
|
eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, (EGLNativeWindowType)dummyWindow, NULL);
|
|
|
|
#elif defined(__ANDROID__)
|
|
|
|
eglSurface = systemInfo.info.android.surface;
|
|
|
|
#else
|
|
|
|
eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, (EGLNativeWindowType)systemInfo.info.win.window, NULL);
|
|
|
|
#endif
|
|
|
|
if (eglSurface == EGL_NO_SURFACE)
|
|
|
|
{
|
|
|
|
eprinterr("eglSurface failure! Error: %x\n", eglGetError());
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, OGLES_VERSION, EGL_NONE };
|
|
|
|
eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, contextAttribs);
|
|
|
|
|
|
|
|
if (eglContext == EGL_NO_CONTEXT) {
|
|
|
|
eprinterr("eglContext failure! Error: %x\n", eglGetError());
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
static int Emulator_InitialiseSDL(char* windowName, int width, int height)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
windowWidth = width;
|
|
|
|
windowHeight = height;
|
2020-03-31 18:55:57 +02:00
|
|
|
|
|
|
|
//Initialise SDL2
|
|
|
|
if (SDL_Init(SDL_INIT_VIDEO) == 0)
|
|
|
|
{
|
|
|
|
#if !defined(RO_DOUBLE_BUFFERED)
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(OGLES)
|
|
|
|
|
|
|
|
#if defined(__ANDROID__)
|
|
|
|
//Override to full screen.
|
|
|
|
SDL_DisplayMode displayMode;
|
|
|
|
if(SDL_GetCurrentDisplayMode(0, &displayMode) == 0)
|
|
|
|
{
|
|
|
|
screenWidth = displayMode.w;
|
|
|
|
windowWidth = displayMode.w;
|
|
|
|
screenHeight = displayMode.h;
|
|
|
|
windowHeight = displayMode.h;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
//SDL_GL_SetAttribute(SDL_GL_CONTEXT_EGL, 1);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, OGLES_VERSION);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
|
|
|
#elif defined(OGL)
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
eprinterr("Error: Failed to initialise SDL\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(OGL)
|
|
|
|
if (Emulator_InitialiseGLContext(windowName) == FALSE)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to Initialise GL Context!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
#elif defined(OGLES)
|
|
|
|
if (Emulator_InitialiseGLESContext(windowName) == FALSE)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to Initialise GLES Context!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
#elif defined(VK)
|
|
|
|
if (Emulator_InitialiseVKContext(windowName) == FALSE)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to Initialise VK Context!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
#elif defined(D3D9)
|
|
|
|
if (Emulator_InitialiseD3D9Context(windowName) == FALSE)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to Initialise D3D9 Context!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int Emulator_InitialiseGLEW()
|
|
|
|
{
|
|
|
|
#if defined(GLEW)
|
|
|
|
glewExperimental = GL_TRUE;
|
|
|
|
|
|
|
|
GLenum err = glewInit();
|
|
|
|
|
|
|
|
if (err != GLEW_OK)
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int Emulator_InitialiseCore()
|
|
|
|
{
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
void Emulator_Initialise(char* windowName, int width, int height)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
|
|
|
eprintf("Initialising Emulator.\n");
|
|
|
|
eprintf("VERSION: %d.%d\n", EMULATOR_MAJOR_VERSION, EMULATOR_MINOR_VERSION);
|
|
|
|
eprintf("Compile Date: %s Time: %s\n", EMULATOR_COMPILE_DATE, EMULATOR_COMPILE_TIME);
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
if (Emulator_InitialiseSDL(windowName, width, height) == FALSE)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
|
|
|
eprinterr("Failed to Intialise SDL\n");
|
|
|
|
Emulator_ShutDown();
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(GLEW)
|
|
|
|
if (Emulator_InitialiseGLEW() == FALSE)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to Intialise GLEW\n");
|
|
|
|
Emulator_ShutDown();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (Emulator_InitialiseCore() == FALSE)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to Intialise Emulator Core.\n");
|
|
|
|
Emulator_ShutDown();
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
if (Emulator_Initialise() == FALSE)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
|
|
|
eprinterr("Failed to Intialise GL.\n");
|
|
|
|
Emulator_ShutDown();
|
|
|
|
}
|
|
|
|
|
|
|
|
//counter_thread = std::thread(Emulator_CounterLoop);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Emulator_CounterLoop()
|
|
|
|
{
|
|
|
|
static int numUpdates = 0;
|
|
|
|
int last_time = 0;
|
|
|
|
|
|
|
|
while (TRUE)
|
|
|
|
{
|
|
|
|
int now = SDL_GetTicks();
|
|
|
|
|
|
|
|
if (now > last_time + 1000)
|
|
|
|
{
|
|
|
|
numUpdates = 0;
|
|
|
|
last_time = now;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (numUpdates++ <= 60)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
//if (!counters[i].IsStopped)
|
|
|
|
{
|
|
|
|
counters[i].cycle += COUNTER_UPDATE_INTERVAL;
|
|
|
|
if (counters[i].target > 0)
|
|
|
|
{
|
|
|
|
counters[i].cycle %= counters[i].target;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Emulator_GenerateLineArray(struct Vertex* vertex, short* p0, short* p1, short* p2, short* p3)
|
|
|
|
{
|
|
|
|
//Copy over position
|
|
|
|
if (p0 != NULL)
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
vertex[0].x = p0[0];
|
|
|
|
vertex[0].y = p0[1];
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (p1 != NULL)
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
vertex[1].x = p1[0];
|
|
|
|
vertex[1].y = p1[1];
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Emulator_GenerateVertexArrayQuad(struct Vertex* vertex, short* p0, short* p1, short* p2, short* p3, short w, short h)
|
|
|
|
{
|
|
|
|
#if defined(PGXP)
|
|
|
|
PGXPVertex* pgxp_vertex_0 = NULL;
|
|
|
|
PGXPVertex* pgxp_vertex_1 = NULL;
|
|
|
|
PGXPVertex* pgxp_vertex_2 = NULL;
|
|
|
|
PGXPVertex* pgxp_vertex_3 = NULL;
|
|
|
|
|
|
|
|
//Locate each vertex based on SXY2 (very slow)
|
|
|
|
for (int i = 0; i < pgxp_vertex_index; i++)
|
|
|
|
{
|
|
|
|
if (p0 != NULL)
|
|
|
|
{
|
|
|
|
if (((unsigned int*)p0)[0] == pgxp_vertex_buffer[i].originalSXY2)
|
|
|
|
{
|
|
|
|
pgxp_vertex_0 = &pgxp_vertex_buffer[i];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p1 != NULL)
|
|
|
|
{
|
|
|
|
if (((unsigned int*)p1)[0] == pgxp_vertex_buffer[i].originalSXY2)
|
|
|
|
{
|
|
|
|
pgxp_vertex_1 = &pgxp_vertex_buffer[i];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p2 != NULL)
|
|
|
|
{
|
|
|
|
if (((unsigned int*)p2)[0] == pgxp_vertex_buffer[i].originalSXY2)
|
|
|
|
{
|
|
|
|
pgxp_vertex_2 = &pgxp_vertex_buffer[i];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p3 != NULL)
|
|
|
|
{
|
|
|
|
if (((unsigned int*)p3)[0] == pgxp_vertex_buffer[i].originalSXY2)
|
|
|
|
{
|
|
|
|
pgxp_vertex_3 = &pgxp_vertex_buffer[i];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//Copy over position
|
|
|
|
if (p0 != NULL)
|
|
|
|
{
|
|
|
|
#if defined(PGXP)
|
|
|
|
if (pgxp_vertex_0 != NULL)
|
|
|
|
{
|
|
|
|
vertex[0].x = pgxp_vertex_0->x;
|
|
|
|
vertex[0].y = pgxp_vertex_0->y;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vertex[0].x = (float)p0[0];
|
|
|
|
vertex[0].y = (float)p0[1];
|
|
|
|
}
|
|
|
|
#else
|
2020-03-31 19:09:22 +02:00
|
|
|
vertex[0].x = p0[0];
|
|
|
|
vertex[0].y = p0[1];
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p1 != NULL)
|
|
|
|
{
|
|
|
|
#if defined(PGXP)
|
|
|
|
if (pgxp_vertex_1 != NULL)
|
|
|
|
{
|
|
|
|
vertex[1].x = pgxp_vertex_1->x;
|
|
|
|
vertex[1].y = pgxp_vertex_1->y;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vertex[1].x = (float)p1[0];
|
|
|
|
vertex[1].y = (float)p1[1];
|
|
|
|
}
|
|
|
|
#else
|
2020-03-31 19:09:22 +02:00
|
|
|
vertex[1].x = p1[0];
|
|
|
|
vertex[1].y = p1[1];
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (p0 != NULL && w != -1 && h != -1)
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
vertex[1].x = p0[0];
|
|
|
|
vertex[1].y = p0[1] + h;
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p2 != NULL)
|
|
|
|
{
|
|
|
|
#if defined(PGXP)
|
|
|
|
if (pgxp_vertex_2 != NULL)
|
|
|
|
{
|
|
|
|
vertex[2].x = pgxp_vertex_2->x;
|
|
|
|
vertex[2].y = pgxp_vertex_2->y;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vertex[2].x = (float)p2[0];
|
|
|
|
vertex[2].y = (float)p2[1];
|
|
|
|
}
|
|
|
|
#else
|
2020-03-31 19:09:22 +02:00
|
|
|
vertex[2].x = p2[0];
|
|
|
|
vertex[2].y = p2[1];
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (p0 != NULL && w != -1 && h != -1)
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
vertex[2].x = p0[0] + w;
|
|
|
|
vertex[2].y = p0[1] + h;
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p3 != NULL)
|
|
|
|
{
|
|
|
|
#if defined(PGXP)
|
|
|
|
if (pgxp_vertex_3 != NULL)
|
|
|
|
{
|
|
|
|
vertex[3].x = pgxp_vertex_3->x;
|
|
|
|
vertex[3].y = pgxp_vertex_3->y;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vertex[3].x = (float)p3[0];
|
|
|
|
vertex[3].y = (float)p3[1];
|
|
|
|
}
|
|
|
|
#else
|
2020-03-31 19:09:22 +02:00
|
|
|
vertex[3].x = p3[0];
|
|
|
|
vertex[3].y = p3[1];
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (p0 != NULL && w != -1 && h != -1)
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
vertex[3].x = p0[0] + w;
|
|
|
|
vertex[3].y = p0[1];
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
void Emulator_GenerateTexcoordArrayQuad(struct Vertex* vertex, unsigned char* uv0, unsigned char* uv1, unsigned char* uv2, unsigned char* uv3, short page, short clut, unsigned char dither)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
|
|
|
#if defined(PGXP) && 0
|
|
|
|
/*
|
|
|
|
Locate polygon in ztable
|
|
|
|
*/
|
|
|
|
|
|
|
|
PGXPPolygon* z0 = NULL;
|
|
|
|
PGXPPolygon* z1 = NULL;
|
|
|
|
PGXPPolygon* z2 = NULL;
|
|
|
|
PGXPPolygon* z3 = NULL;
|
|
|
|
|
|
|
|
//Can speed this up by storing last index found and using as start iter
|
|
|
|
for (int i = pgxp_polgon_table_index; i > -1; i--)
|
|
|
|
{
|
|
|
|
if (uv0 != NULL)
|
|
|
|
{
|
|
|
|
if (((unsigned int*)uv0)[0] == pgxp_polygons[i].originalSXY)
|
|
|
|
{
|
|
|
|
z0 = &pgxp_polygons[i];
|
|
|
|
//z0->bUsed = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uv1 != NULL)
|
|
|
|
{
|
|
|
|
if (((unsigned int*)uv1)[0] == pgxp_polygons[i].originalSXY)
|
|
|
|
{
|
|
|
|
z1 = &pgxp_polygons[i];
|
|
|
|
//z1->bUsed = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uv2 != NULL)
|
|
|
|
{
|
|
|
|
if (((unsigned int*)uv2)[0] == pgxp_polygons[i].originalSXY)
|
|
|
|
{
|
|
|
|
z2 = &pgxp_polygons[i];
|
|
|
|
//z2->bUsed = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uv3 != NULL)
|
|
|
|
{
|
|
|
|
if (((unsigned int*)uv3)[0] == pgxp_polygons[i].originalSXY)
|
|
|
|
{
|
|
|
|
z3 = &pgxp_polygons[i];
|
|
|
|
//z3->bUsed = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((z0 != NULL || uv0 == NULL) && (z1 != NULL || uv1 == NULL) && (z2 != NULL || uv2 == NULL) && (z3 != NULL || uv3 == NULL))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Copy over uvs
|
|
|
|
if (uv0 != NULL)
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
vertex[0].x = p0[0];
|
|
|
|
vertex[0].y = p0[1];
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (uv1 != NULL)
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
vertex[1].x = p1[0];
|
|
|
|
vertex[1].y = p1[1];
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (w != -1 && h != -1)
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
vertex[1].x = p0[0];
|
|
|
|
vertex[1].y = p0[1] + h;
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uv2 != NULL)
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
vertex[2].x = p2[0];
|
|
|
|
vertex[2].y = p2[1];
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (w != -1 && h != -1)
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
vertex[2].x = p0[0] + w;
|
|
|
|
vertex[2].y = p0[1] + h;
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uv3 != NULL)
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
vertex[3].x = p3[0];
|
|
|
|
vertex[3].y = p3[1];
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (w != -1 && h != -1)
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
vertex[3].x = p0[0] + w;
|
|
|
|
vertex[3].y = p0[1];
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
2020-03-31 19:09:22 +02:00
|
|
|
assert(uv0);
|
|
|
|
if (!uv1) uv1 = uv0;
|
|
|
|
if (!uv2) uv2 = uv0;
|
|
|
|
if (!uv3) uv3 = uv0;
|
|
|
|
|
|
|
|
const unsigned char bright = 2;
|
|
|
|
|
|
|
|
vertex[0].u = uv0[0];
|
|
|
|
vertex[0].v = uv0[1];
|
|
|
|
vertex[0].bright = bright;
|
|
|
|
vertex[0].dither = dither;
|
|
|
|
vertex[0].page = page;
|
|
|
|
vertex[0].clut = clut;
|
|
|
|
|
|
|
|
vertex[1].u = uv1[0];
|
|
|
|
vertex[1].v = uv1[1];
|
|
|
|
vertex[1].bright = bright;
|
|
|
|
vertex[1].dither = dither;
|
|
|
|
vertex[1].page = page;
|
|
|
|
vertex[1].clut = clut;
|
|
|
|
|
|
|
|
vertex[2].u = uv2[0];
|
|
|
|
vertex[2].v = uv2[1];
|
|
|
|
vertex[2].bright = bright;
|
|
|
|
vertex[2].dither = dither;
|
|
|
|
vertex[2].page = page;
|
|
|
|
vertex[2].clut = clut;
|
|
|
|
|
|
|
|
vertex[3].u = uv3[0];
|
|
|
|
vertex[3].v = uv3[1];
|
|
|
|
vertex[3].bright = bright;
|
|
|
|
vertex[3].dither = dither;
|
|
|
|
vertex[3].page = page;
|
|
|
|
vertex[3].clut = clut;
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
void Emulator_GenerateTexcoordArrayRect(struct Vertex* vertex, unsigned char* uv, short page, short clut, short w, short h)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
assert(uv);
|
|
|
|
//assert(int(uv[0]) + w <= 255);
|
|
|
|
//assert(int(uv[1]) + h <= 255);
|
|
|
|
// TODO
|
|
|
|
if (int(uv[0]) + w > 255) w = 255 - uv[0];
|
|
|
|
if (int(uv[1]) + h > 255) h = 255 - uv[1];
|
|
|
|
|
|
|
|
const unsigned char bright = 2;
|
|
|
|
const unsigned char dither = 0;
|
|
|
|
|
|
|
|
vertex[0].u = uv[0];
|
|
|
|
vertex[0].v = uv[1];
|
|
|
|
vertex[0].bright = bright;
|
|
|
|
vertex[0].dither = dither;
|
|
|
|
vertex[0].page = page;
|
|
|
|
vertex[0].clut = clut;
|
|
|
|
|
|
|
|
vertex[1].u = uv[0];
|
|
|
|
vertex[1].v = uv[1] + h;
|
|
|
|
vertex[1].bright = bright;
|
|
|
|
vertex[1].dither = dither;
|
|
|
|
vertex[1].page = page;
|
|
|
|
vertex[1].clut = clut;
|
|
|
|
|
|
|
|
vertex[2].u = uv[0] + w;
|
|
|
|
vertex[2].v = uv[1] + h;
|
|
|
|
vertex[2].bright = bright;
|
|
|
|
vertex[2].dither = dither;
|
|
|
|
vertex[2].page = page;
|
|
|
|
vertex[2].clut = clut;
|
|
|
|
|
|
|
|
vertex[3].u = uv[0] + w;
|
|
|
|
vertex[3].v = uv[1];
|
|
|
|
vertex[3].bright = bright;
|
|
|
|
vertex[3].dither = dither;
|
|
|
|
vertex[3].page = page;
|
|
|
|
vertex[3].clut = clut;
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
void Emulator_GenerateTexcoordArrayZero(struct Vertex* vertex, unsigned char dither)
|
|
|
|
{
|
|
|
|
const unsigned char bright = 1;
|
|
|
|
|
|
|
|
vertex[0].u = 0;
|
|
|
|
vertex[0].v = 0;
|
|
|
|
vertex[0].bright = bright;
|
|
|
|
vertex[0].dither = dither;
|
|
|
|
vertex[0].page = 0;
|
|
|
|
vertex[0].clut = 0;
|
|
|
|
|
|
|
|
vertex[1].u = 0;
|
|
|
|
vertex[1].v = 0;
|
|
|
|
vertex[1].bright = bright;
|
|
|
|
vertex[1].dither = dither;
|
|
|
|
vertex[1].page = 0;
|
|
|
|
vertex[1].clut = 0;
|
|
|
|
|
|
|
|
vertex[2].u = 0;
|
|
|
|
vertex[2].v = 0;
|
|
|
|
vertex[2].bright = bright;
|
|
|
|
vertex[2].dither = dither;
|
|
|
|
vertex[2].page = 0;
|
|
|
|
vertex[2].clut = 0;
|
|
|
|
|
|
|
|
vertex[3].u = 0;
|
|
|
|
vertex[3].v = 0;
|
|
|
|
vertex[3].bright = bright;
|
|
|
|
vertex[3].dither = dither;
|
|
|
|
vertex[3].page = 0;
|
|
|
|
vertex[3].clut = 0;
|
|
|
|
}
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
void Emulator_GenerateColourArrayQuad(struct Vertex* vertex, unsigned char* col0, unsigned char* col1, unsigned char* col2, unsigned char* col3)
|
|
|
|
{
|
|
|
|
assert(col0);
|
|
|
|
if (!col1) col1 = col0;
|
|
|
|
if (!col2) col2 = col0;
|
|
|
|
if (!col3) col3 = col0;
|
|
|
|
|
|
|
|
vertex[0].r = col0[0];
|
|
|
|
vertex[0].g = col0[1];
|
|
|
|
vertex[0].b = col0[2];
|
|
|
|
vertex[0].a = 255;
|
|
|
|
|
|
|
|
vertex[1].r = col1[0];
|
|
|
|
vertex[1].g = col1[1];
|
|
|
|
vertex[1].b = col1[2];
|
|
|
|
vertex[1].a = 255;
|
|
|
|
|
|
|
|
vertex[2].r = col2[0];
|
|
|
|
vertex[2].g = col2[1];
|
|
|
|
vertex[2].b = col2[2];
|
|
|
|
vertex[2].a = 255;
|
|
|
|
|
|
|
|
vertex[3].r = col3[0];
|
|
|
|
vertex[3].g = col3[1];
|
|
|
|
vertex[3].b = col3[2];
|
|
|
|
vertex[3].a = 255;
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
ShaderID g_gte_shader;
|
|
|
|
ShaderID g_blit_shader;
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
#if defined(OGLES) || defined(OGL)
|
|
|
|
GLint u_Projection;
|
|
|
|
|
2020-04-01 01:27:09 +02:00
|
|
|
// TODO: 8 bit palettized images and full mode textures
|
2020-03-31 19:09:22 +02:00
|
|
|
const char* gte_shader =
|
|
|
|
"varying vec4 v_texcoord;\n"
|
|
|
|
"varying vec4 v_color;\n"
|
|
|
|
"varying vec4 v_page_clut;\n"
|
2020-04-01 14:36:02 +02:00
|
|
|
"varying vec2 v_pagetype;\n"
|
2020-03-31 19:09:22 +02:00
|
|
|
"#ifdef VERTEX\n"
|
|
|
|
" attribute vec4 a_position;\n"
|
|
|
|
" attribute vec4 a_texcoord; // uv, color multiplier, dither\n"
|
|
|
|
" attribute vec4 a_color;\n"
|
|
|
|
" uniform mat4 Projection;\n"
|
|
|
|
" void main() {\n"
|
|
|
|
" v_texcoord = a_texcoord;\n"
|
|
|
|
" v_color = a_color;\n"
|
|
|
|
" v_color.xyz *= a_texcoord.z;\n"
|
2020-04-01 14:36:02 +02:00
|
|
|
" v_pagetype.x = floor(a_position.z / 128.0) + 1.0;\n"
|
|
|
|
" v_pagetype.y = (4.0 - floor(a_position.z / 128.0));\n"
|
2020-03-31 19:09:22 +02:00
|
|
|
" v_page_clut.x = fract(a_position.z / 16.0) * 1024.0;\n"
|
|
|
|
" v_page_clut.y = floor(a_position.z / 16.0) * 256.0;\n"
|
|
|
|
" v_page_clut.z = fract(a_position.w / 64.0);\n"
|
|
|
|
" v_page_clut.w = floor(a_position.w / 64.0) / 512.0;\n"
|
|
|
|
" gl_Position = Projection * vec4(a_position.xy, 0.0, 1.0);\n"
|
|
|
|
" }\n"
|
|
|
|
"#else\n"
|
|
|
|
" uniform sampler2D s_texture;\n"
|
|
|
|
" void main() {\n"
|
2020-04-01 14:36:02 +02:00
|
|
|
" vec2 uv = (v_texcoord.xy * vec2(v_pagetype.x * 0.25, 1.0) + v_page_clut.xy) * vec2(1.0 / 1024.0, 1.0 / 512.0);\n"
|
2020-03-31 19:09:22 +02:00
|
|
|
" vec2 comp = texture2D(s_texture, uv).rg;\n"
|
|
|
|
" int index = int(fract(v_texcoord.x / 4.0 + 0.0001) * 4.0);\n"
|
|
|
|
"\n"
|
2020-04-01 14:36:02 +02:00
|
|
|
" float v = comp[index / 2] * (255.0 / pow(16.0,v_pagetype.x));\n"
|
2020-03-31 19:09:22 +02:00
|
|
|
" float f = floor(v);\n"
|
|
|
|
"\n"
|
2020-04-01 14:36:02 +02:00
|
|
|
" vec2 c = vec2( (v - f) * pow(16.0,v_pagetype.x), f );\n"
|
2020-03-31 19:09:22 +02:00
|
|
|
"\n"
|
|
|
|
" vec2 clut_pos = v_page_clut.zw;\n"
|
|
|
|
" clut_pos.x += mix(c[0], c[1], fract(float(index) / 2.0) * 2.0) / 1024.0;\n"
|
|
|
|
" vec2 clut_color = texture2D(s_texture, clut_pos).rg * 255.0;\n"
|
|
|
|
"\n"
|
|
|
|
" float color_16 = clut_color.y * 256.0 + clut_color.x;\n"
|
|
|
|
" vec4 color = fract(floor(color_16 / vec4(1.0, 32.0, 1024.0, 32768.0)) / 32.0);\n"
|
|
|
|
"\n"
|
|
|
|
" fragColor = color * v_color;\n"
|
2020-03-31 23:08:47 +02:00
|
|
|
" if (fragColor.r == 0.0 && fragColor.b == 0.0 && fragColor.b == 0.0 && fragColor.a == 0.0) { discard; }\n"
|
|
|
|
"\n"
|
2020-03-31 19:09:22 +02:00
|
|
|
" mat4 dither = mat4(\n"
|
|
|
|
" -4.0, +0.0, -3.0, +1.0,\n"
|
|
|
|
" +2.0, -2.0, +3.0, -1.0,\n"
|
|
|
|
" -3.0, +1.0, -4.0, +0.0,\n"
|
|
|
|
" +3.0, -1.0, +2.0, -2.0) / 255.0;\n"
|
|
|
|
" ivec2 dc = ivec2(fract(gl_FragCoord.xy / 4.0) * 4.0);\n"
|
|
|
|
" fragColor.xyz += vec3(dither[dc.x][dc.y] * v_texcoord.w);\n"
|
|
|
|
"\n"
|
|
|
|
" }\n"
|
|
|
|
"#endif\n";
|
|
|
|
|
|
|
|
const char* blit_shader =
|
|
|
|
"varying vec4 v_texcoord;\n"
|
|
|
|
"#ifdef VERTEX\n"
|
|
|
|
" attribute vec4 a_position;\n"
|
|
|
|
" attribute vec4 a_texcoord;\n"
|
|
|
|
" void main() {\n"
|
|
|
|
" v_texcoord = a_texcoord * vec4(8.0 / 1024.0, 8.0 / 512.0, 0.0, 0.0);\n"
|
|
|
|
" gl_Position = vec4(a_position.xy, 0.0, 1.0);\n"
|
|
|
|
" }\n"
|
|
|
|
"#else\n"
|
|
|
|
" uniform sampler2D s_texture;\n"
|
|
|
|
" void main() {\n"
|
|
|
|
" vec2 color_rg = texture2D(s_texture, v_texcoord.xy).rg * 255.0;\n"
|
|
|
|
" float color_16 = color_rg.y * 256.0 + color_rg.x;\n"
|
|
|
|
" fragColor = fract(floor(color_16 / vec4(1.0, 32.0, 1024.0, 32768.0)) / 32.0);\n"
|
|
|
|
" fragColor.a = 1.0;\n"
|
|
|
|
" }\n"
|
|
|
|
"#endif\n";
|
|
|
|
|
|
|
|
void Shader_CheckShaderStatus(GLuint shader)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
char info[1024];
|
|
|
|
glGetShaderInfoLog(shader, sizeof(info), NULL, info);
|
|
|
|
if (info[0] && strlen(info) > 8)
|
|
|
|
{
|
|
|
|
eprinterr("%s\n", info);
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
}
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
void Shader_CheckProgramStatus(GLuint program)
|
|
|
|
{
|
|
|
|
char info[1024];
|
|
|
|
glGetProgramInfoLog(program, sizeof(info), NULL, info);
|
|
|
|
if (info[0] && strlen(info) > 8)
|
|
|
|
{
|
|
|
|
eprinterr("%s\n", info);
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
}
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
ShaderID Shader_Compile(const char *source)
|
|
|
|
{
|
2020-03-31 18:55:57 +02:00
|
|
|
#if defined(ES2_SHADERS)
|
2020-03-31 19:09:22 +02:00
|
|
|
const char *GLSL_HEADER_VERT =
|
|
|
|
"#version 100\n"
|
|
|
|
"precision lowp int;\n"
|
|
|
|
"precision highp float;\n"
|
|
|
|
"#define VERTEX\n";
|
|
|
|
|
|
|
|
const char *GLSL_HEADER_FRAG =
|
|
|
|
"#version 100\n"
|
|
|
|
"precision lowp int;\n"
|
|
|
|
"precision highp float;\n"
|
|
|
|
"#define fragColor gl_FragColor\n";
|
2020-03-31 18:55:57 +02:00
|
|
|
#elif defined(ES3_SHADERS)
|
2020-03-31 19:09:22 +02:00
|
|
|
const char *GLSL_HEADER_VERT =
|
|
|
|
"#version 300 es\n"
|
|
|
|
"precision lowp int;\n"
|
|
|
|
"precision highp float;\n"
|
|
|
|
"#define VERTEX\n"
|
|
|
|
"#define varying out\n"
|
|
|
|
"#define attribute in\n"
|
|
|
|
"#define texture2D texture\n";
|
|
|
|
|
|
|
|
const char *GLSL_HEADER_FRAG =
|
|
|
|
"#version 300 es\n"
|
|
|
|
"precision lowp int;\n"
|
|
|
|
"precision highp float;\n"
|
|
|
|
"#define varying in\n"
|
|
|
|
"#define texture2D texture\n"
|
|
|
|
"out vec4 fragColor;\n";
|
|
|
|
#else
|
|
|
|
const char *GLSL_HEADER_VERT =
|
|
|
|
"#version 330\n"
|
|
|
|
"#define VERTEX\n"
|
|
|
|
"#define varying out\n"
|
|
|
|
"#define attribute in\n"
|
|
|
|
"#define texture2D texture\n";
|
|
|
|
|
|
|
|
const char *GLSL_HEADER_FRAG =
|
|
|
|
"#version 330\n"
|
|
|
|
"#define varying in\n"
|
|
|
|
"#define texture2D texture\n"
|
|
|
|
"out vec4 fragColor;\n";
|
|
|
|
#endif
|
|
|
|
|
|
|
|
const char *vs_list[] = { GLSL_HEADER_VERT, source };
|
|
|
|
const char *fs_list[] = { GLSL_HEADER_FRAG, source };
|
|
|
|
|
|
|
|
GLuint program = glCreateProgram();
|
|
|
|
|
|
|
|
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
|
|
|
glShaderSource(vertexShader, 2, vs_list, NULL);
|
|
|
|
glCompileShader(vertexShader);
|
|
|
|
Shader_CheckShaderStatus(vertexShader);
|
|
|
|
glAttachShader(program, vertexShader);
|
|
|
|
glDeleteShader(vertexShader);
|
|
|
|
|
|
|
|
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
|
|
|
|
glShaderSource(fragmentShader, 2, fs_list, NULL);
|
|
|
|
glCompileShader(fragmentShader);
|
|
|
|
Shader_CheckShaderStatus(fragmentShader);
|
|
|
|
glAttachShader(program, fragmentShader);
|
|
|
|
glDeleteShader(fragmentShader);
|
|
|
|
|
|
|
|
glBindAttribLocation(program, a_position, "a_position");
|
|
|
|
glBindAttribLocation(program, a_texcoord, "a_texcoord");
|
|
|
|
glBindAttribLocation(program, a_color, "a_color");
|
|
|
|
|
|
|
|
glLinkProgram(program);
|
|
|
|
Shader_CheckProgramStatus(program);
|
|
|
|
|
|
|
|
GLint sampler = 0;
|
|
|
|
glUseProgram(program);
|
|
|
|
glUniform1iv(glGetUniformLocation(program, "s_texture"), 1, &sampler);
|
|
|
|
glUseProgram(0);
|
|
|
|
|
|
|
|
return program;
|
|
|
|
}
|
2020-03-31 18:55:57 +02:00
|
|
|
#elif defined(D3D9)
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
#include "shaders/gte_shader_vs.h"
|
|
|
|
#include "shaders/gte_shader_ps.h"
|
|
|
|
#include "shaders/blit_shader_vs.h"
|
|
|
|
#include "shaders/blit_shader_ps.h"
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
// shader registers
|
|
|
|
const int u_Projection = 0;
|
|
|
|
const int u_PageClut = 4;
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
LPDIRECT3DVERTEXDECLARATION9 vertexDecl;
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
#define Shader_Compile(name) Shader_Compile_Internal((DWORD*)name##_vs, (DWORD*)name##_ps)
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
ShaderID Shader_Compile_Internal(const DWORD *vs_data, const DWORD *ps_data)
|
|
|
|
{
|
|
|
|
ShaderID shader;
|
|
|
|
HRESULT hr;
|
|
|
|
hr = d3ddev->CreateVertexShader(vs_data, &shader.VS);
|
|
|
|
assert(!FAILED(hr));
|
|
|
|
hr = d3ddev->CreatePixelShader(ps_data, &shader.PS);
|
|
|
|
assert(!FAILED(hr));
|
|
|
|
return shader;
|
|
|
|
}
|
|
|
|
#elif
|
|
|
|
#error
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
void Emulator_CreateGlobalShaders()
|
|
|
|
{
|
|
|
|
g_gte_shader = Shader_Compile(gte_shader);
|
|
|
|
g_blit_shader = Shader_Compile(blit_shader);
|
|
|
|
|
2020-03-31 18:55:57 +02:00
|
|
|
#if defined(OGL) || defined(OGLES)
|
2020-03-31 19:09:22 +02:00
|
|
|
u_Projection = glGetUniformLocation(g_gte_shader, "Projection");
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned short vram[VRAM_WIDTH * VRAM_HEIGHT];
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
void Emulator_GenerateCommonTextures()
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
unsigned int pixelData = 0xFFFFFFFF;
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
|
|
|
glGenTextures(1, &whiteTexture);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, whiteTexture);
|
|
|
|
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, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &pixelData);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
#elif defined(D3D9)
|
|
|
|
HRESULT hr = d3ddev->CreateTexture(1, 1, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &whiteTexture, NULL);
|
|
|
|
assert(!FAILED(hr));
|
|
|
|
D3DLOCKED_RECT rect;
|
|
|
|
hr = whiteTexture->LockRect(0, &rect, NULL, 0);
|
|
|
|
assert(!FAILED(hr));
|
|
|
|
memcpy(rect.pBits, &pixelData, sizeof(pixelData));
|
|
|
|
whiteTexture->UnlockRect(0);
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
2020-03-31 19:09:22 +02:00
|
|
|
}
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
int Emulator_Initialise()
|
|
|
|
{
|
2020-03-31 18:55:57 +02:00
|
|
|
SDL_memset(vram, 0, VRAM_WIDTH * VRAM_HEIGHT * sizeof(unsigned short));
|
2020-03-31 19:09:22 +02:00
|
|
|
Emulator_GenerateCommonTextures();
|
|
|
|
Emulator_CreateGlobalShaders();
|
2020-03-31 18:55:57 +02:00
|
|
|
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
2020-03-31 19:09:22 +02:00
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
glGenTextures(1, &vramTexture);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, vramTexture);
|
|
|
|
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_RG8, VRAM_WIDTH, VRAM_HEIGHT, 0, GL_RG, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
|
|
|
glGenBuffers(1, &dynamic_vertex_buffer);
|
|
|
|
glGenVertexArrays(1, &dynamic_vertex_array);
|
|
|
|
glBindVertexArray(dynamic_vertex_array);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, dynamic_vertex_buffer);
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * MAX_NUM_POLY_BUFFER_VERTICES, NULL, GL_DYNAMIC_DRAW);
|
|
|
|
glEnableVertexAttribArray(a_position);
|
|
|
|
glEnableVertexAttribArray(a_texcoord);
|
|
|
|
|
|
|
|
glEnableVertexAttribArray(a_color);
|
|
|
|
glVertexAttribPointer(a_position, 4, GL_SHORT, GL_FALSE, sizeof(Vertex), &((Vertex*)NULL)->x);
|
|
|
|
glVertexAttribPointer(a_texcoord, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(Vertex), &((Vertex*)NULL)->u);
|
|
|
|
glVertexAttribPointer(a_color, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), &((Vertex*)NULL)->r);
|
|
|
|
glBindVertexArray(0);
|
|
|
|
#elif defined(D3D9)
|
|
|
|
d3ddev->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
if (FAILED(d3ddev->CreateTexture(VRAM_WIDTH, VRAM_HEIGHT, 1, 0, D3DFMT_A8L8, D3DPOOL_MANAGED, &vramTexture, NULL)))
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
|
|
|
eprinterr("Failed to create render target texture!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
#define OFFSETOF(T, E) ((size_t)&(((T*)0)->E))
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
const D3DVERTEXELEMENT9 VERTEX_DECL[] = {
|
|
|
|
{0, OFFSETOF(Vertex, x), D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, // a_position
|
|
|
|
{0, OFFSETOF(Vertex, u), D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, // a_texcoord
|
|
|
|
{0, OFFSETOF(Vertex, r), D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0}, // a_color
|
|
|
|
D3DDECL_END()
|
|
|
|
};
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
d3ddev->CreateVertexDeclaration(VERTEX_DECL, &vertexDecl);
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
#undef OFFSETOF
|
|
|
|
#else
|
|
|
|
#error
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
Emulator_ResetDevice();
|
|
|
|
|
|
|
|
return TRUE;
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
#if 0
|
2020-03-31 18:55:57 +02:00
|
|
|
unsigned int getMemoryType(unsigned int typeBits, VkMemoryPropertyFlags properties, VkBool32* memTypeFound)
|
|
|
|
{
|
|
|
|
memTypeFound = NULL;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < deviceMemoryProperties.memoryTypeCount; i++)
|
|
|
|
{
|
|
|
|
if ((typeBits & 1) == 1)
|
|
|
|
{
|
|
|
|
if ((deviceMemoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
|
|
|
|
{
|
|
|
|
if (memTypeFound)
|
|
|
|
{
|
|
|
|
*memTypeFound = true;
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
typeBits >>= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (memTypeFound)
|
|
|
|
{
|
|
|
|
*memTypeFound = false;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
eprinterr("Could not find matching memory type!\n");
|
|
|
|
assert(FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
int Emulator_Initialise()
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
|
|
|
//d3ddev->GetRenderTarget(0, &g_defaultRenderTarget);
|
|
|
|
//d3ddev->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
|
|
|
|
//d3ddev->SetRenderState(D3DRS_BLENDFACTOR, D3DCOLOR_RGBA(64, 64, 64, 128));
|
|
|
|
//d3ddev->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
|
|
|
|
|
|
|
|
/* Initialise VRAM */
|
|
|
|
SDL_memset(vram, 0, VRAM_WIDTH * VRAM_HEIGHT * sizeof(unsigned short));
|
|
|
|
|
|
|
|
/* Generate NULL white texture */
|
|
|
|
//Emulator_GenerateAndBindNullWhite();///@TODO
|
|
|
|
|
|
|
|
vramFrameBuffer.width = VRAM_WIDTH;
|
|
|
|
vramFrameBuffer.height = VRAM_HEIGHT;
|
|
|
|
|
|
|
|
// Find a suitable depth format
|
|
|
|
VkFormat fbDepthFormat;
|
|
|
|
//VkBool32 validDepthFormat = vks::tools::getSupportedDepthFormat(physicalDevice, &fbDepthFormat);
|
|
|
|
//assert(validDepthFormat);
|
|
|
|
|
|
|
|
// Color attachment
|
|
|
|
VkImageCreateInfo imageCreateInfo;
|
|
|
|
memset(&imageCreateInfo, 0, sizeof(VkImageCreateInfo));
|
|
|
|
|
|
|
|
imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
|
|
|
|
imageCreateInfo.format = VK_FORMAT_R5G5B5A1_UNORM_PACK16;
|
|
|
|
imageCreateInfo.extent.width = vramFrameBuffer.width;
|
|
|
|
imageCreateInfo.extent.height = vramFrameBuffer.height;
|
|
|
|
imageCreateInfo.extent.depth = 1;
|
|
|
|
imageCreateInfo.mipLevels = 1;
|
|
|
|
imageCreateInfo.arrayLayers = 1;
|
|
|
|
imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
|
|
|
|
|
|
// We will sample directly from the color attachment
|
|
|
|
imageCreateInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
|
|
|
|
|
|
VkMemoryAllocateInfo memoryAllocateInfo;
|
|
|
|
memset(&memoryAllocateInfo, 0, sizeof(VkMemoryAllocateInfo));
|
|
|
|
VkMemoryRequirements memReqs;
|
|
|
|
memset(&memReqs, 0, sizeof(VkMemoryRequirements));
|
|
|
|
if (vkCreateImage(device, &imageCreateInfo, nullptr, &vramFrameBuffer.color.image) != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to create vram image!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
vkGetImageMemoryRequirements(device, vramFrameBuffer.color.image, &memReqs);
|
|
|
|
memoryAllocateInfo.allocationSize = memReqs.size;
|
|
|
|
memoryAllocateInfo.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, NULL);
|
|
|
|
if (vkAllocateMemory(device, &memoryAllocateInfo, nullptr, &vramFrameBuffer.color.mem) != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to allocate vram image memory!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vkBindImageMemory(device, vramFrameBuffer.color.image, vramFrameBuffer.color.mem, 0) != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to bind vram image memory!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkImageViewCreateInfo colorImageView;
|
|
|
|
memset(&colorImageView, 0, sizeof(VkImageViewCreateInfo));
|
|
|
|
|
|
|
|
colorImageView.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
|
|
colorImageView.format = VK_FORMAT_R5G5B5A1_UNORM_PACK16;
|
|
|
|
colorImageView.subresourceRange = {};
|
|
|
|
colorImageView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
colorImageView.subresourceRange.baseMipLevel = 0;
|
|
|
|
colorImageView.subresourceRange.levelCount = 1;
|
|
|
|
colorImageView.subresourceRange.baseArrayLayer = 0;
|
|
|
|
colorImageView.subresourceRange.layerCount = 1;
|
|
|
|
colorImageView.image = vramFrameBuffer.color.image;
|
|
|
|
|
|
|
|
if (vkCreateImageView(device, &colorImageView, nullptr, &vramFrameBuffer.color.view) != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to create vram view image!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create sampler to sample from the attachment in the fragment shader
|
|
|
|
VkSamplerCreateInfo samplerInfo;
|
|
|
|
memset(&samplerInfo, 0, sizeof(VkSamplerCreateInfo));
|
|
|
|
|
|
|
|
samplerInfo.magFilter = VK_FILTER_LINEAR;
|
|
|
|
samplerInfo.minFilter = VK_FILTER_LINEAR;
|
|
|
|
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
|
|
|
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
|
|
samplerInfo.addressModeV = samplerInfo.addressModeU;
|
|
|
|
samplerInfo.addressModeW = samplerInfo.addressModeU;
|
|
|
|
samplerInfo.mipLodBias = 0.0f;
|
|
|
|
samplerInfo.maxAnisotropy = 1.0f;
|
|
|
|
samplerInfo.minLod = 0.0f;
|
|
|
|
samplerInfo.maxLod = 1.0f;
|
|
|
|
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
|
|
|
|
if (vkCreateSampler(device, &samplerInfo, nullptr, &vramFrameBuffer.sampler) != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to create vram sampler!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Depth stencil attachment
|
|
|
|
imageCreateInfo.format = fbDepthFormat;
|
|
|
|
imageCreateInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
|
|
|
if (vkCreateImage(device, &imageCreateInfo, nullptr, &vramFrameBuffer.depth.image) != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to create depth image!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
vkGetImageMemoryRequirements(device, vramFrameBuffer.depth.image, &memReqs);
|
|
|
|
memoryAllocateInfo.allocationSize = memReqs.size;
|
|
|
|
memoryAllocateInfo.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, FALSE);
|
|
|
|
if (vkAllocateMemory(device, &memoryAllocateInfo, nullptr, &vramFrameBuffer.depth.mem) != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to allocate depth image memory!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (vkBindImageMemory(device, vramFrameBuffer.depth.image, vramFrameBuffer.depth.mem, 0) != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to allocate bind depth image memory!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkImageViewCreateInfo depthStencilView;
|
|
|
|
memset(&depthStencilView, 0, sizeof(VkImageViewCreateInfo));
|
|
|
|
|
|
|
|
depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
|
|
depthStencilView.format = fbDepthFormat;
|
|
|
|
depthStencilView.flags = 0;
|
|
|
|
depthStencilView.subresourceRange = {};
|
|
|
|
depthStencilView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
|
|
depthStencilView.subresourceRange.baseMipLevel = 0;
|
|
|
|
depthStencilView.subresourceRange.levelCount = 1;
|
|
|
|
depthStencilView.subresourceRange.baseArrayLayer = 0;
|
|
|
|
depthStencilView.subresourceRange.layerCount = 1;
|
|
|
|
depthStencilView.image = vramFrameBuffer.depth.image;
|
|
|
|
|
|
|
|
if (vkCreateImageView(device, &depthStencilView, nullptr, &vramFrameBuffer.depth.view) != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to create depth image view!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
// Create a separate render pass for the offscreen rendering as it may differ from the one used for scene rendering
|
|
|
|
VkAttachmentDescription attchmentDescriptions[2];
|
|
|
|
memset(&attchmentDescriptions[0], 0, sizeof(VkAttachmentDescription) * 2);
|
|
|
|
|
|
|
|
// Color attachment
|
|
|
|
attchmentDescriptions[0].format = VK_FORMAT_R5G5B5A1_UNORM_PACK16;
|
|
|
|
attchmentDescriptions[0].samples = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
attchmentDescriptions[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
|
|
attchmentDescriptions[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
|
|
attchmentDescriptions[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
|
|
attchmentDescriptions[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
|
|
attchmentDescriptions[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
attchmentDescriptions[0].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
|
|
// Depth attachment
|
|
|
|
attchmentDescriptions[1].format = fbDepthFormat;
|
|
|
|
attchmentDescriptions[1].samples = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
attchmentDescriptions[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
|
|
attchmentDescriptions[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
|
|
attchmentDescriptions[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
|
|
attchmentDescriptions[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
|
|
attchmentDescriptions[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
attchmentDescriptions[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
|
|
|
|
|
|
VkAttachmentReference colorReference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
|
|
|
|
VkAttachmentReference depthReference = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
|
|
|
|
|
|
|
|
VkSubpassDescription subpassDescription = {};
|
|
|
|
subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
|
|
subpassDescription.colorAttachmentCount = 1;
|
|
|
|
subpassDescription.pColorAttachments = &colorReference;
|
|
|
|
subpassDescription.pDepthStencilAttachment = &depthReference;
|
|
|
|
|
|
|
|
// Use subpass dependencies for layout transitions
|
|
|
|
VkSubpassDependency subPassDependencies[2];
|
|
|
|
memset(&subPassDependencies[0], 0, sizeof(VkSubpassDependency) * 2);
|
|
|
|
|
|
|
|
subPassDependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
|
|
|
|
subPassDependencies[0].dstSubpass = 0;
|
|
|
|
subPassDependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
|
|
|
subPassDependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
|
|
subPassDependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
|
|
subPassDependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
|
|
|
subPassDependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
|
|
|
|
|
|
|
|
subPassDependencies[1].srcSubpass = 0;
|
|
|
|
subPassDependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
|
|
|
|
subPassDependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
|
|
subPassDependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
|
|
|
subPassDependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
|
|
|
subPassDependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
|
|
subPassDependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
|
|
|
|
|
|
|
|
// Create the actual renderpass
|
|
|
|
VkRenderPassCreateInfo renderPassInfo = {};
|
|
|
|
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
|
|
|
renderPassInfo.attachmentCount = 2;
|
|
|
|
renderPassInfo.pAttachments = &attchmentDescriptions[0];
|
|
|
|
renderPassInfo.subpassCount = 1;
|
|
|
|
renderPassInfo.pSubpasses = &subpassDescription;
|
|
|
|
renderPassInfo.dependencyCount = 2;
|
|
|
|
renderPassInfo.pDependencies = &subPassDependencies[0];
|
|
|
|
|
|
|
|
if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &vramFrameBuffer.renderPass) != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to create render pass!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkImageView attachments[2];
|
|
|
|
attachments[0] = vramFrameBuffer.color.view;
|
|
|
|
attachments[1] = vramFrameBuffer.depth.view;
|
|
|
|
|
|
|
|
VkFramebufferCreateInfo fbufCreateInfo;
|
|
|
|
memset(&fbufCreateInfo, 0, sizeof(VkFramebufferCreateInfo));
|
|
|
|
fbufCreateInfo.renderPass = vramFrameBuffer.renderPass;
|
|
|
|
fbufCreateInfo.attachmentCount = 2;
|
|
|
|
fbufCreateInfo.pAttachments = attachments;
|
|
|
|
fbufCreateInfo.width = vramFrameBuffer.width;
|
|
|
|
fbufCreateInfo.height = vramFrameBuffer.height;
|
|
|
|
fbufCreateInfo.layers = 1;
|
|
|
|
|
|
|
|
if (vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &vramFrameBuffer.frameBuffer) != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
eprinterr("Failed to create frame buffer!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fill a descriptor for later use in a descriptor set
|
|
|
|
vramFrameBuffer.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
|
|
vramFrameBuffer.descriptor.imageView = vramFrameBuffer.color.view;
|
|
|
|
vramFrameBuffer.descriptor.sampler = vramFrameBuffer.sampler;
|
|
|
|
|
|
|
|
Emulator_CreateGlobalShaders();
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
void Emulator_SetTexture(TextureID texture)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
if (g_texturelessMode) {
|
|
|
|
texture = whiteTexture;
|
|
|
|
}
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
if (g_lastBoundTexture == texture) {
|
|
|
|
return;
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
2020-03-31 19:09:22 +02:00
|
|
|
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
|
|
|
glBindTexture(GL_TEXTURE_2D, texture);
|
2020-03-31 18:55:57 +02:00
|
|
|
#elif defined(D3D9)
|
|
|
|
d3ddev->SetTexture(0, texture);
|
|
|
|
#endif
|
2020-03-31 19:09:22 +02:00
|
|
|
|
|
|
|
g_lastBoundTexture = texture;
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
void Emulator_DestroyTexture(TextureID texture)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
2020-03-31 19:09:22 +02:00
|
|
|
glDeleteTextures(1, &texture);
|
2020-03-31 18:55:57 +02:00
|
|
|
#elif defined(D3D9)
|
2020-03-31 19:09:22 +02:00
|
|
|
texture->Release();
|
|
|
|
#elif
|
|
|
|
#error
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
extern void Emulator_Clear(int x, int y, int w, int h, unsigned char r, unsigned char g, unsigned char b)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
// TODO clear rect if it's necessary
|
2020-03-31 18:55:57 +02:00
|
|
|
#if defined(OGL) || defined(OGLES)
|
2020-03-31 19:09:22 +02:00
|
|
|
glClearColor(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
#elif defined(D3D9)
|
|
|
|
d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, 0xFF000000 | (r << 16) | (g << 8) | (b), 1.0f, 0);
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
void Emulator_SetShader(const ShaderID &shader)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
2020-03-31 19:09:22 +02:00
|
|
|
glUseProgram(shader);
|
2020-03-31 18:55:57 +02:00
|
|
|
#elif defined(D3D9)
|
2020-03-31 19:09:22 +02:00
|
|
|
d3ddev->SetVertexShader(shader.VS);
|
|
|
|
d3ddev->SetPixelShader(shader.PS);
|
|
|
|
#elif
|
|
|
|
#error
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
}
|
2020-03-31 19:09:22 +02:00
|
|
|
|
2020-03-31 18:55:57 +02:00
|
|
|
#define NOFILE 0
|
|
|
|
|
|
|
|
#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__)
|
|
|
|
void Emulator_SaveVRAM(const char* outputFileName, int x, int y, int width, int height, int bReadFromFrameBuffer)
|
|
|
|
{
|
|
|
|
#if NOFILE
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
|
|
|
FILE* f = fopen(outputFileName, "wb");
|
|
|
|
if (f == NULL)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
unsigned char TGAheader[12] = { 0,0,2,0,0,0,0,0,0,0,0,0 };
|
|
|
|
unsigned char header[6];
|
|
|
|
header[0] = (width % 256);
|
|
|
|
header[1] = (width / 256);
|
|
|
|
header[2] = (height % 256);
|
|
|
|
header[3] = (height / 256);
|
|
|
|
header[4] = 16;
|
|
|
|
header[5] = 0;
|
|
|
|
|
|
|
|
fwrite(TGAheader, sizeof(unsigned char), 12, f);
|
|
|
|
fwrite(header, sizeof(unsigned char), 6, f);
|
|
|
|
|
|
|
|
//512 const is hdd sector size
|
|
|
|
int numSectorsToWrite = (width * height * sizeof(unsigned short)) / 512;
|
|
|
|
int numRemainingSectorsToWrite = (width * height * sizeof(unsigned short)) % 512;
|
|
|
|
|
|
|
|
for (int i = 0; i < numSectorsToWrite; i++)
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
fwrite(&vram[i * 512 / sizeof(unsigned short)], 512, 1, f);
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < numRemainingSectorsToWrite; i++)
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
fwrite(&vram[numSectorsToWrite * 512 / sizeof(unsigned short)], numRemainingSectorsToWrite, 1, f);
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
#elif defined(D3D9)
|
2020-03-31 19:09:22 +02:00
|
|
|
//D3DXSaveSurfaceToFile(outputFileName, D3DXIFF_TGA, vramFrameBuffer, NULL, NULL);
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
bool vram_need_update = true;
|
|
|
|
|
|
|
|
void Emulator_CopyVRAM(unsigned short *src, int x, int y, int w, int h, int dst_x, int dst_y)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
vram_need_update = true;
|
|
|
|
|
|
|
|
int stride = w;
|
|
|
|
|
|
|
|
if (!src) {
|
|
|
|
src = vram;
|
|
|
|
stride = VRAM_WIDTH;
|
|
|
|
}
|
|
|
|
|
|
|
|
src += x + y * stride;
|
|
|
|
|
|
|
|
unsigned short *dst = vram + dst_x + dst_y * VRAM_WIDTH;
|
|
|
|
|
|
|
|
for (int y = 0; y < h; y++) {
|
|
|
|
SDL_memcpy(dst, src, w * 2);
|
|
|
|
dst += VRAM_WIDTH;
|
|
|
|
src += stride;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Emulator_UpdateVRAM()
|
|
|
|
{
|
|
|
|
if (!vram_need_update) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
vram_need_update = false;
|
|
|
|
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
|
|
|
glBindTexture(GL_TEXTURE_2D, vramTexture);
|
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, GL_RG, GL_UNSIGNED_BYTE, vram);
|
|
|
|
#elif defined(D3D9)
|
|
|
|
D3DLOCKED_RECT rect;
|
|
|
|
HRESULT hr = vramTexture->LockRect(0, &rect, NULL, 0);
|
|
|
|
assert(!FAILED(hr));
|
|
|
|
memcpy(rect.pBits, vram, VRAM_WIDTH * VRAM_HEIGHT * sizeof(unsigned short));
|
|
|
|
vramTexture->UnlockRect(0);
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
2020-03-31 19:09:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Emulator_BlitVRAM()
|
|
|
|
{
|
|
|
|
if (activeDispEnv.isinter)
|
|
|
|
{
|
|
|
|
//Emulator_Clear(0, 0, activeDispEnv.disp.w, activeDispEnv.disp.h, 128, 128, 128);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Emulator_SetShader(g_blit_shader);
|
|
|
|
|
|
|
|
u_char l = activeDispEnv.disp.x / 8;
|
|
|
|
u_char t = activeDispEnv.disp.y / 8;
|
|
|
|
u_char r = activeDispEnv.disp.w / 8 + l;
|
|
|
|
u_char b = activeDispEnv.disp.h / 8 + t;
|
|
|
|
|
|
|
|
Vertex blit_vertices[] =
|
|
|
|
{
|
|
|
|
{ +1, +1, 0, 0, r, t, 0, 0, 0, 0, 0, 0 },
|
|
|
|
{ -1, -1, 0, 0, l, b, 0, 0, 0, 0, 0, 0 },
|
|
|
|
{ -1, +1, 0, 0, l, t, 0, 0, 0, 0, 0, 0 },
|
|
|
|
|
|
|
|
{ +1, -1, 0, 0, r, b, 0, 0, 0, 0, 0, 0 },
|
|
|
|
{ -1, -1, 0, 0, l, b, 0, 0, 0, 0, 0, 0 },
|
|
|
|
{ +1, +1, 0, 0, r, t, 0, 0, 0, 0, 0, 0 },
|
|
|
|
};
|
|
|
|
|
|
|
|
Emulator_UpdateVertexBuffer(blit_vertices, 6);
|
|
|
|
Emulator_SetBlendMode(0, 0);
|
|
|
|
Emulator_DrawTriangles(0, 2);
|
|
|
|
|
|
|
|
Emulator_SetShader(g_gte_shader);
|
|
|
|
}
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
void Emulator_DoPollEvent()
|
|
|
|
{
|
2020-03-31 18:55:57 +02:00
|
|
|
SDL_Event event;
|
|
|
|
while (SDL_PollEvent(&event))
|
|
|
|
{
|
|
|
|
switch (event.type)
|
|
|
|
{
|
|
|
|
case SDL_JOYDEVICEADDED:
|
|
|
|
if (SDL_IsGameController(event.jbutton.which))
|
|
|
|
{
|
|
|
|
padHandle[event.jbutton.which] = SDL_GameControllerOpen(event.jbutton.which);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SDL_JOYDEVICEREMOVED:
|
|
|
|
SDL_GameControllerClose(padHandle[event.jbutton.which]);
|
|
|
|
break;
|
|
|
|
case SDL_QUIT:
|
|
|
|
Emulator_ShutDown();
|
|
|
|
break;
|
|
|
|
case SDL_WINDOWEVENT:
|
|
|
|
switch (event.window.event)
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
case SDL_WINDOWEVENT_RESIZED:
|
|
|
|
windowWidth = event.window.data1;
|
|
|
|
windowHeight = event.window.data2;
|
|
|
|
Emulator_ResetDevice();
|
|
|
|
break;
|
2020-03-31 18:55:57 +02:00
|
|
|
case SDL_WINDOWEVENT_CLOSE:
|
|
|
|
Emulator_ShutDown();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
bool begin_scene_flag = false;
|
|
|
|
bool vbo_was_dirty_flag = false;
|
|
|
|
|
|
|
|
bool Emulator_BeginScene()
|
|
|
|
{
|
|
|
|
Emulator_DoPollEvent();
|
|
|
|
|
|
|
|
if (begin_scene_flag)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
assert(!begin_scene_flag);
|
|
|
|
|
|
|
|
g_lastBoundTexture = NULL;
|
|
|
|
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
|
|
|
glBindVertexArray(dynamic_vertex_array);
|
|
|
|
#elif defined(D3D9)
|
|
|
|
d3ddev->BeginScene();
|
|
|
|
d3ddev->SetVertexDeclaration(vertexDecl);
|
|
|
|
d3ddev->SetStreamSource(0, dynamic_vertex_buffer, 0, sizeof(Vertex));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
Emulator_UpdateVRAM();
|
|
|
|
Emulator_SetTexture(vramTexture);
|
|
|
|
Emulator_SetViewPort(0, 0, windowWidth, windowHeight);
|
|
|
|
|
|
|
|
Emulator_SetShader(g_gte_shader);
|
|
|
|
Emulator_Ortho2D(0.0f, activeDispEnv.disp.w, activeDispEnv.disp.h, 0.0f, 0.0f, 1.0f);
|
|
|
|
|
|
|
|
begin_scene_flag = true;
|
|
|
|
|
|
|
|
if (g_wireframeMode)
|
|
|
|
{
|
|
|
|
Emulator_SetWireframe(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-03-31 18:55:57 +02:00
|
|
|
#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__)
|
|
|
|
void Emulator_TakeScreenshot()
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
unsigned char* pixels = new unsigned char[windowWidth * windowHeight * 4];
|
2020-03-31 18:55:57 +02:00
|
|
|
#if defined(OGL) || defined(OGLES)
|
2020-03-31 19:09:22 +02:00
|
|
|
glReadPixels(0, 0, windowWidth, windowHeight, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
2020-03-31 19:09:22 +02:00
|
|
|
SDL_Surface* surface = SDL_CreateRGBSurfaceFrom(pixels, windowWidth, windowHeight, 8 * 4, windowWidth * 4, 0, 0, 0, 0);
|
2020-03-31 18:55:57 +02:00
|
|
|
SDL_SaveBMP(surface, "SCREENSHOT.BMP");
|
|
|
|
SDL_FreeSurface(surface);
|
|
|
|
|
|
|
|
delete[] pixels;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
///@FIXME keyboardState only accessible if padInitDirect called! Let the emulator manage input not the sub library!
|
|
|
|
void Emulator_DoDebugKeys()
|
|
|
|
{
|
|
|
|
static unsigned int currentTime;
|
|
|
|
static unsigned int lastTime;
|
|
|
|
|
|
|
|
currentTime = SDL_GetTicks();
|
|
|
|
|
|
|
|
if (currentTime > lastTime + 60)
|
|
|
|
{
|
|
|
|
if (keyboardState[SDL_SCANCODE_BACKSPACE])
|
|
|
|
{
|
|
|
|
if (g_swapInterval != 0)
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
g_swapInterval = 0;
|
|
|
|
Emulator_ResetDevice();
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
if (g_swapInterval != SWAP_INTERVAL)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
g_swapInterval = SWAP_INTERVAL;
|
|
|
|
Emulator_ResetDevice();
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (keyboardState[SDL_SCANCODE_1])
|
|
|
|
{
|
|
|
|
g_wireframeMode ^= 1;
|
2020-03-31 19:00:33 +02:00
|
|
|
eprintf("wireframe mode: %d\n", g_wireframeMode);
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (keyboardState[SDL_SCANCODE_2])
|
|
|
|
{
|
|
|
|
g_texturelessMode ^= 1;
|
2020-03-31 19:00:33 +02:00
|
|
|
eprintf("textureless mode: %d\n", g_texturelessMode);
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__)
|
|
|
|
if (keyboardState[SDL_SCANCODE_3])
|
|
|
|
{
|
2020-03-31 19:00:33 +02:00
|
|
|
eprintf("saving screenshot\n");
|
2020-03-31 18:55:57 +02:00
|
|
|
Emulator_TakeScreenshot();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-03-31 19:00:33 +02:00
|
|
|
if (keyboardState[SDL_SCANCODE_4])
|
|
|
|
{
|
|
|
|
eprintf("saving VRAM.TGA\n");
|
|
|
|
Emulator_SaveVRAM("VRAM.TGA", 0, 0, VRAM_WIDTH, VRAM_HEIGHT, TRUE);
|
|
|
|
}
|
|
|
|
|
2020-03-31 18:55:57 +02:00
|
|
|
lastTime = currentTime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Emulator_UpdateInput()
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
// also poll events here
|
|
|
|
Emulator_DoPollEvent();
|
|
|
|
|
2020-03-31 18:55:57 +02:00
|
|
|
//Update pad
|
|
|
|
if (SDL_NumJoysticks() > 0)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < MAX_CONTROLLERS; i++)
|
|
|
|
{
|
|
|
|
if (padHandle[i] != NULL)
|
|
|
|
{
|
|
|
|
padData[i][0] = 0;
|
|
|
|
padData[i][1] = 0x41;//?
|
|
|
|
((unsigned short*)padData[i])[1] = UpdateGameControllerInput(padHandle[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Update keyboard
|
|
|
|
if (padData[0] != NULL)
|
|
|
|
{
|
|
|
|
((unsigned short*)padData[0])[1] = UpdateKeyboardInput();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(__ANDROID__)
|
|
|
|
///@TODO SDL_NumJoysticks always reports > 0 for some reason on Android.
|
|
|
|
((unsigned short*)padData[0])[1] = UpdateKeyboardInput();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
Emulator_DoDebugKeys();
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int Emulator_GetFPS()
|
|
|
|
{
|
|
|
|
#define FPS_INTERVAL 1.0
|
|
|
|
|
|
|
|
static unsigned int lastTime = SDL_GetTicks();
|
|
|
|
static unsigned int currentFps = 0;
|
|
|
|
static unsigned int passedFrames = 0;
|
|
|
|
|
|
|
|
passedFrames++;
|
|
|
|
if (lastTime < SDL_GetTicks() - FPS_INTERVAL * 1000)
|
|
|
|
{
|
|
|
|
lastTime = SDL_GetTicks();
|
|
|
|
currentFps = passedFrames;
|
|
|
|
passedFrames = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return currentFps;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Emulator_SwapWindow()
|
|
|
|
{
|
|
|
|
#if defined(RO_DOUBLE_BUFFERED)
|
|
|
|
#if defined(OGL)
|
|
|
|
SDL_GL_SwapWindow(g_window);
|
|
|
|
#elif defined(OGLES)
|
|
|
|
eglSwapBuffers(eglDisplay, eglSurface);
|
|
|
|
#elif defined(D3D9)
|
2020-03-31 19:09:22 +02:00
|
|
|
if (g_swapInterval == 2) {
|
|
|
|
// D3D9 support D3DPRESENT_INTERVAL_TWO only in fullscreen mode
|
|
|
|
// so we add additional one frame wait here, bad hack
|
|
|
|
Sleep(16 + 4); // 16 ms - 60 Hz
|
|
|
|
}
|
|
|
|
|
|
|
|
if (d3ddev->Present(NULL, NULL, NULL, NULL) == D3DERR_DEVICELOST) {
|
|
|
|
Emulator_ResetDevice();
|
|
|
|
}
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
glFinish();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void Emulator_EndScene()
|
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
if (!begin_scene_flag)
|
|
|
|
return;
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
if (!vbo_was_dirty_flag)
|
|
|
|
return;
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
assert(begin_scene_flag);
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
if (g_wireframeMode)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
Emulator_SetWireframe(false);
|
|
|
|
}
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
#if defined(OGL) || defined(OGLES)
|
|
|
|
glBindVertexArray(0);
|
2020-03-31 18:55:57 +02:00
|
|
|
#elif defined(D3D9)
|
|
|
|
d3ddev->EndScene();
|
|
|
|
#endif
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
begin_scene_flag = false;
|
|
|
|
vbo_was_dirty_flag = false;
|
|
|
|
|
2020-03-31 18:55:57 +02:00
|
|
|
Emulator_SwapWindow();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Emulator_ShutDown()
|
|
|
|
{
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
2020-03-31 19:09:22 +02:00
|
|
|
Emulator_DestroyTexture(vramTexture);
|
|
|
|
Emulator_DestroyTexture(whiteTexture);
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
|
|
|
|
|
|
|
|
#if defined(VK)
|
|
|
|
vkDestroySurfaceKHR(instance, surface, 0);
|
|
|
|
vkDestroyInstance(instance, NULL);
|
|
|
|
#elif defined(D3D9)
|
2020-03-31 19:09:22 +02:00
|
|
|
d3ddev->Release();
|
|
|
|
d3d->Release();
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
SDL_DestroyWindow(g_window);
|
|
|
|
SDL_Quit();
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
static int g_PreviousBlendMode = -1;
|
|
|
|
static int g_PreviousSemiTrans = 0;
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
void Emulator_SetBlendMode(int mode, int semiTransparent)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
if (semiTransparent)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
//If previous wasn't semi trans, enable blend
|
|
|
|
if (g_PreviousSemiTrans == 0)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
2020-03-31 19:09:22 +02:00
|
|
|
glEnable(GL_BLEND);
|
2020-03-31 18:55:57 +02:00
|
|
|
#elif defined(D3D9)
|
2020-03-31 19:09:22 +02:00
|
|
|
d3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
if (g_PreviousBlendMode != mode)
|
|
|
|
{
|
|
|
|
switch (mode)
|
|
|
|
{
|
|
|
|
case BM_AVERAGE:
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
|
|
|
glBlendColor(0.5f, 0.5f, 0.5f, 0.5f);
|
|
|
|
glBlendFunc(GL_CONSTANT_COLOR, GL_CONSTANT_COLOR);
|
|
|
|
glBlendEquation(GL_FUNC_ADD);
|
2020-03-31 18:55:57 +02:00
|
|
|
#elif defined(D3D9)
|
2020-03-31 19:09:22 +02:00
|
|
|
d3ddev->SetRenderState(D3DRS_BLENDFACTOR, D3DCOLOR_RGBA(128, 128, 128, 128));
|
|
|
|
d3ddev->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
|
|
|
|
d3ddev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_BLENDFACTOR);
|
|
|
|
d3ddev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_BLENDFACTOR);
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case BM_ADD:
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
2020-03-31 19:09:22 +02:00
|
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
|
|
|
glBlendEquation(GL_FUNC_ADD);
|
2020-03-31 18:55:57 +02:00
|
|
|
#elif defined(D3D9)
|
|
|
|
d3ddev->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
|
|
|
|
d3ddev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
|
|
|
|
d3ddev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case BM_SUBTRACT:
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
2020-03-31 19:09:22 +02:00
|
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
|
|
|
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
|
2020-03-31 18:55:57 +02:00
|
|
|
#elif defined(D3D9)
|
|
|
|
d3ddev->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_REVSUBTRACT);
|
|
|
|
d3ddev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
|
|
|
|
d3ddev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case BM_ADD_QUATER_SOURCE:
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
2020-03-31 19:09:22 +02:00
|
|
|
glBlendColor(0.25f, 0.25f, 0.25f, 0.25f);
|
|
|
|
glBlendFunc(GL_CONSTANT_COLOR, GL_ONE);
|
|
|
|
glBlendEquation(GL_FUNC_ADD);
|
2020-03-31 18:55:57 +02:00
|
|
|
#elif defined(D3D9)
|
2020-03-31 19:09:22 +02:00
|
|
|
d3ddev->SetRenderState(D3DRS_BLENDFACTOR, D3DCOLOR_RGBA(64, 64, 64, 64));
|
2020-03-31 18:55:57 +02:00
|
|
|
d3ddev->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
|
|
|
|
d3ddev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_BLENDFACTOR);
|
|
|
|
d3ddev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_PreviousBlendMode = mode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//If previous was semi trans disable blend
|
|
|
|
if (g_PreviousSemiTrans)
|
|
|
|
{
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
#elif defined(D3D9)
|
|
|
|
d3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_PreviousSemiTrans = semiTransparent;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Emulator_Ortho2D(float left, float right, float bottom, float top, float znear, float zfar)
|
|
|
|
{
|
|
|
|
float a = 2.0f / (right - left);
|
|
|
|
float b = 2.0f / (top - bottom);
|
2020-03-31 19:09:22 +02:00
|
|
|
float c = 2.0f / (znear - zfar);
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
float x = (left + right) / (left - right);
|
|
|
|
float y = (bottom + top) / (bottom - top);
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
#if defined(OGL) || defined(OGLES) // -1..1
|
|
|
|
float z = (znear + zfar) / (znear - zfar);
|
|
|
|
#elif defined(D3D9) // 0..1
|
|
|
|
float z = znear / (znear - zfar);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
float ortho[16] = {
|
2020-03-31 18:55:57 +02:00
|
|
|
a, 0, 0, 0,
|
|
|
|
0, b, 0, 0,
|
|
|
|
0, 0, c, 0,
|
2020-03-31 19:09:22 +02:00
|
|
|
x, y, z, 1
|
2020-03-31 18:55:57 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
2020-03-31 19:09:22 +02:00
|
|
|
glUniformMatrix4fv(u_Projection, 1, GL_FALSE, &ortho[0]);
|
2020-03-31 18:55:57 +02:00
|
|
|
#elif defined(D3D9)
|
2020-03-31 19:09:22 +02:00
|
|
|
d3ddev->SetVertexShaderConstantF(u_Projection, ortho, 4);
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void Emulator_SetPGXPVertexCount(int vertexCount)
|
|
|
|
{
|
|
|
|
#if defined(PGXP)
|
|
|
|
pgxp_vertex_count = vertexCount;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void Emulator_SetViewPort(int x, int y, int width, int height)
|
|
|
|
{
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
|
|
|
glViewport(x, y, width, height);
|
|
|
|
#elif defined(D3D9)
|
2020-03-31 19:09:22 +02:00
|
|
|
D3DVIEWPORT9 viewport;
|
|
|
|
viewport.X = x;
|
|
|
|
viewport.Y = y;
|
|
|
|
viewport.Width = width;
|
|
|
|
viewport.Height = height;
|
|
|
|
viewport.MinZ = 0.0f;
|
|
|
|
viewport.MaxZ = 1.0f;
|
|
|
|
d3ddev->SetViewport(&viewport);
|
2020-03-31 18:55:57 +02:00
|
|
|
#elif defined(VK)
|
|
|
|
VkViewport viewPort;
|
|
|
|
viewPort.x = x;
|
|
|
|
viewPort.y = y;
|
|
|
|
viewPort.width = width;
|
|
|
|
viewPort.height = height;
|
|
|
|
viewPort.minDepth = 0.0f;
|
|
|
|
viewPort.maxDepth = 1.0f;
|
|
|
|
//assert(FALSE);//Unfinished see below.
|
|
|
|
//vkCmdSetViewport(draw_cmd, 0, 1, &viewport);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
void Emulator_SetRenderTarget(const RenderTargetID &frameBufferObject)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
2020-03-31 19:09:22 +02:00
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject);
|
2020-03-31 18:55:57 +02:00
|
|
|
#elif defined(D3D9)
|
2020-03-31 19:09:22 +02:00
|
|
|
d3ddev->SetRenderTarget(0, frameBufferObject);
|
2020-03-31 18:55:57 +02:00
|
|
|
#elif defined(VK)
|
2020-03-31 19:09:22 +02:00
|
|
|
#error
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
void Emulator_SetWireframe(bool enable)
|
|
|
|
{
|
|
|
|
#if defined(OGL)
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, enable ? GL_LINE : GL_FILL);
|
2020-03-31 18:55:57 +02:00
|
|
|
#elif defined(D3D9)
|
2020-03-31 19:09:22 +02:00
|
|
|
d3ddev->SetRenderState(D3DRS_FILLMODE, enable ? D3DFILL_WIREFRAME : D3DFILL_SOLID);
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
2020-03-31 19:09:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Emulator_UpdateVertexBuffer(const Vertex *vertices, int num_vertices)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
2020-03-31 19:09:22 +02:00
|
|
|
assert(num_vertices <= MAX_NUM_POLY_BUFFER_VERTICES);
|
2020-03-31 18:55:57 +02:00
|
|
|
#if defined(OGL) || defined(OGLES)
|
2020-03-31 19:09:22 +02:00
|
|
|
glBufferSubData(GL_ARRAY_BUFFER, 0, num_vertices * sizeof(Vertex), vertices);
|
2020-03-31 18:55:57 +02:00
|
|
|
#elif defined(D3D9)
|
2020-03-31 19:09:22 +02:00
|
|
|
void *ptr;
|
|
|
|
dynamic_vertex_buffer->Lock(0, 0, &ptr, D3DLOCK_DISCARD);
|
|
|
|
memcpy(ptr, vertices, num_vertices * sizeof(Vertex));
|
|
|
|
dynamic_vertex_buffer->Unlock();
|
|
|
|
#else
|
|
|
|
#error
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
2020-03-31 19:09:22 +02:00
|
|
|
|
|
|
|
vbo_was_dirty_flag = true;
|
2020-03-31 18:55:57 +02:00
|
|
|
}
|
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
void Emulator_DrawTriangles(int start_vertex, int triangles)
|
2020-03-31 18:55:57 +02:00
|
|
|
{
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
2020-03-31 19:09:22 +02:00
|
|
|
glDrawArrays(GL_TRIANGLES, start_vertex, triangles * 3);
|
2020-03-31 18:55:57 +02:00
|
|
|
#elif defined(D3D9)
|
2020-03-31 19:09:22 +02:00
|
|
|
d3ddev->DrawPrimitive(D3DPT_TRIANGLELIST, start_vertex, triangles);
|
|
|
|
#else
|
|
|
|
#error
|
|
|
|
#endif
|
|
|
|
}
|
2020-03-31 18:55:57 +02:00
|
|
|
|
2020-03-31 19:09:22 +02:00
|
|
|
void Emulator_DrawLines(int start_vertex, int lines)
|
|
|
|
{
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
|
|
|
glDrawArrays(GL_LINES, start_vertex, lines * 2);
|
|
|
|
#elif defined(D3D9)
|
|
|
|
d3ddev->DrawPrimitive(D3DPT_LINELIST, start_vertex, lines);
|
|
|
|
#else
|
|
|
|
#error
|
2020-03-31 18:55:57 +02:00
|
|
|
#endif
|
2020-03-31 19:09:22 +02:00
|
|
|
}
|