vulkan: moltenvk support on macOS - WIP
sdl: save/restore window position in config
This commit is contained in:
parent
46ae8f92cc
commit
da3ed74c4e
|
@ -23,6 +23,12 @@ jobs:
|
|||
- name: Set up build environment (macos-latest)
|
||||
run: |
|
||||
brew install ccache libao libomp pulseaudio zlib ldid
|
||||
curl https://sdk.lunarg.com/sdk/download/1.2.189.0/mac/vulkansdk-macos-1.2.189.0.dmg --output vulkansdk-macos-1.2.189.0.dmg
|
||||
hdiutil attach ./vulkansdk-macos-1.2.189.0.dmg
|
||||
pushd /Volumes/vulkansdk-macos-1.2.189.0
|
||||
sudo ./InstallVulkan.app/Contents/MacOS/InstallVulkan --root ~/VulkanSDK/1.2.189.0 --accept-licenses --default-answer --confirm-command install
|
||||
popd
|
||||
echo "VULKAN_SDK=$HOME/VulkanSDK/1.2.189.0/macOS" >> $GITHUB_ENV
|
||||
echo "/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
|
||||
echo "CCACHE_DIR=/tmp/ccache" >> $GITHUB_ENV
|
||||
if: matrix.config.os == 'macos-latest'
|
||||
|
|
|
@ -124,7 +124,8 @@ if(MSVC)
|
|||
else()
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fno-strict-aliasing>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-Wall>)
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-Wall>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-Wno-nullability-completeness>)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||
|
@ -197,7 +198,7 @@ option(BUILD_SHARED_LIBS "Build shared library" OFF)
|
|||
add_subdirectory(core/deps/glm)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE glm::glm)
|
||||
|
||||
if(NOT APPLE AND USE_VULKAN)
|
||||
if(USE_VULKAN)
|
||||
option(BUILD_EXTERNAL "Build external dependencies in /External" OFF)
|
||||
add_subdirectory(core/deps/glslang)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE SPIRV)
|
||||
|
@ -263,7 +264,7 @@ if(NOT LIBRETRO)
|
|||
endif()
|
||||
|
||||
find_package(Lua)
|
||||
if(LUA_FOUND)
|
||||
if(NOT APPLE AND LUA_FOUND)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_LUA)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${LUA_INCLUDE_DIR} core/deps/luabridge/Source)
|
||||
list(TRANSFORM LUA_LIBRARIES REPLACE "\.dll" "")
|
||||
|
@ -933,7 +934,7 @@ if(NOT (APPLE OR ANDROID OR USE_GLES OR USE_GLES2))
|
|||
core/rend/gl4/gles.cpp)
|
||||
endif()
|
||||
|
||||
if(USE_VULKAN AND NOT APPLE)
|
||||
if(USE_VULKAN)
|
||||
if(NOT LIBRETRO)
|
||||
if(ANDROID)
|
||||
set(VOLK_STATIC_DEFINES VK_USE_PLATFORM_ANDROID_KHR)
|
||||
|
@ -941,6 +942,8 @@ if(USE_VULKAN AND NOT APPLE)
|
|||
set(VOLK_STATIC_DEFINES VK_USE_PLATFORM_XLIB_KHR)
|
||||
elseif(WIN32)
|
||||
set(VOLK_STATIC_DEFINES VK_USE_PLATFORM_WIN32_KHR)
|
||||
elseif(APPLE)
|
||||
set(VOLK_STATIC_DEFINES VK_USE_PLATFORM_MACOS_MVK)
|
||||
endif()
|
||||
|
||||
set(VOLK_PULL_IN_VULKAN OFF)
|
||||
|
@ -1264,13 +1267,9 @@ if(NOT LIBRETRO)
|
|||
COMMAND rm -rf ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE}/Flycast.app ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE}/Flycast.swiftmodule)
|
||||
else()
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
shell/apple/emulator-osx/emulator-osx/AppDelegate.swift
|
||||
shell/apple/emulator-osx/emulator-osx/EmuGLView.swift
|
||||
shell/apple/emulator-osx/emulator-osx/osx-main.mm
|
||||
shell/apple/emulator-osx/emulator-osx/osx_gamepad.h
|
||||
shell/apple/emulator-osx/emulator-osx/osx_keyboard.h)
|
||||
|
||||
set(XIB shell/apple/emulator-osx/emulator-osx/Base.lproj/MainMenu.xib)
|
||||
shell/apple/emulator-osx/emulator-osx/SDLMain.h
|
||||
shell/apple/emulator-osx/emulator-osx/SDLMain.mm
|
||||
shell/apple/emulator-osx/emulator-osx/osx-main.mm)
|
||||
set(ASSETS shell/apple/emulator-osx/emulator-osx/Images.xcassets)
|
||||
target_sources(${PROJECT_NAME} PRIVATE ${XIB} ${ASSETS})
|
||||
source_group("Resources" FILES ${XIB} ${ASSETS})
|
||||
|
@ -1290,8 +1289,6 @@ if(NOT LIBRETRO)
|
|||
MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "1"
|
||||
MACOSX_BUNDLE_COPYRIGHT "Copyright © 2019 Flycast contributors. All rights reserved."
|
||||
XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "shell/apple/emulator-osx/emulator-osx/emulator-osx-Bridging-Header.h"
|
||||
RESOURCE "${XIB}"
|
||||
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
|
||||
BUILD_WITH_INSTALL_RPATH TRUE
|
||||
INSTALL_RPATH "@loader_path/../Frameworks"
|
||||
|
@ -1301,8 +1298,19 @@ if(NOT LIBRETRO)
|
|||
find_library(FOUNDATION_LIBRARY Foundation)
|
||||
find_library(AUDIO_TOOLBOX_LIBRARY AudioToolbox)
|
||||
find_library(MULTITOUCH_SUPPORT_LIBRARY MultitouchSupport /System/Library/PrivateFrameworks)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE ${AUDIO_UNIT_LIBRARY} ${FOUNDATION_LIBRARY} ${AUDIO_TOOLBOX_LIBRARY} ${MULTITOUCH_SUPPORT_LIBRARY})
|
||||
find_library(OPENGL_LIBRARY OpenGL)
|
||||
find_library(IOSURFACE_LIBRARY IOSurface)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
${AUDIO_UNIT_LIBRARY}
|
||||
${FOUNDATION_LIBRARY}
|
||||
${AUDIO_TOOLBOX_LIBRARY}
|
||||
${MULTITOUCH_SUPPORT_LIBRARY}
|
||||
${OPENGL_LIBRARY}
|
||||
${IOSURFACE_LIBRARY})
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "$ENV{VULKAN_SDK}/lib/libMoltenVK.dylib"
|
||||
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Flycast.app/Contents/Frameworks/libvulkan.dylib)
|
||||
endif()
|
||||
elseif(UNIX OR NINTENDO_SWITCH)
|
||||
if(NOT BUILD_TESTING)
|
||||
|
|
|
@ -157,7 +157,7 @@ void gui_init()
|
|||
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f);
|
||||
//ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
|
||||
//IM_ASSERT(font != NULL);
|
||||
#if !(defined(_WIN32) || defined(__APPLE__) || defined(__SWITCH__)) || defined(TARGET_IPHONE)
|
||||
#if !defined(_WIN32) && !defined(__SWITCH__)
|
||||
scaling = std::max(1.f, screen_dpi / 100.f * 0.75f);
|
||||
// Limit scaling on small low-res screens
|
||||
if (settings.display.width <= 640 || settings.display.height <= 480)
|
||||
|
|
|
@ -28,8 +28,13 @@ BufferData::BufferData(vk::DeviceSize size, const vk::BufferUsageFlags& usage, c
|
|||
VulkanContext *context = VulkanContext::Instance();
|
||||
buffer = context->GetDevice().createBufferUnique(vk::BufferCreateInfo(vk::BufferCreateFlags(), size, usage));
|
||||
VmaAllocationCreateInfo allocInfo = {
|
||||
VMA_ALLOCATION_CREATE_MAPPED_BIT,
|
||||
(propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent) ? VMA_ALLOCATION_CREATE_MAPPED_BIT : (VmaAllocationCreateFlags)0,
|
||||
(propertyFlags & vk::MemoryPropertyFlagBits::eDeviceLocal) ? VmaMemoryUsage::VMA_MEMORY_USAGE_GPU_ONLY : VmaMemoryUsage::VMA_MEMORY_USAGE_CPU_TO_GPU
|
||||
};
|
||||
#ifdef __APPLE__
|
||||
if (!(propertyFlags & vk::MemoryPropertyFlagBits::eDeviceLocal))
|
||||
// cpu memory management is fucked up with moltenvk
|
||||
allocInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
|
||||
#endif
|
||||
allocation = context->GetAllocator().AllocateForBuffer(*buffer, allocInfo);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,13 @@
|
|||
struct BufferData
|
||||
{
|
||||
BufferData(vk::DeviceSize size, const vk::BufferUsageFlags& usage,
|
||||
const vk::MemoryPropertyFlags& propertyFlags = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
|
||||
const vk::MemoryPropertyFlags& propertyFlags =
|
||||
vk::MemoryPropertyFlagBits::eHostVisible
|
||||
#ifndef __APPLE__
|
||||
// host coherent memory not supported on apple platforms
|
||||
| vk::MemoryPropertyFlagBits::eHostCoherent
|
||||
#endif
|
||||
);
|
||||
~BufferData()
|
||||
{
|
||||
buffer.reset();
|
||||
|
@ -33,16 +39,17 @@ struct BufferData
|
|||
|
||||
void upload(u32 size, const void *data, u32 bufOffset = 0) const
|
||||
{
|
||||
verify((m_propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent) && (m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible));
|
||||
verify((bool)(m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible));
|
||||
verify(bufOffset + size <= bufferSize);
|
||||
|
||||
void* dataPtr = (u8 *)allocation.MapMemory() + bufOffset;
|
||||
memcpy(dataPtr, data, size);
|
||||
allocation.UnmapMemory();
|
||||
}
|
||||
|
||||
void upload(size_t count, const u32 *sizes, const void * const *data, u32 bufOffset = 0) const
|
||||
{
|
||||
verify((m_propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent) && (m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible));
|
||||
verify((bool)(m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible));
|
||||
|
||||
u32 totalSize = 0;
|
||||
for (size_t i = 0; i < count; i++)
|
||||
|
@ -55,15 +62,17 @@ struct BufferData
|
|||
memcpy(dataPtr, data[i], sizes[i]);
|
||||
dataPtr = (u8 *)dataPtr + sizes[i];
|
||||
}
|
||||
allocation.UnmapMemory();
|
||||
}
|
||||
|
||||
void download(u32 size, void *data, u32 bufOffset = 0) const
|
||||
{
|
||||
verify((m_propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent) && (m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible));
|
||||
verify((bool)(m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible));
|
||||
verify(bufOffset + size <= bufferSize);
|
||||
|
||||
void* dataPtr = (u8 *)allocation.MapMemory() + bufOffset;
|
||||
memcpy(data, dataPtr, size);
|
||||
allocation.UnmapMemory();
|
||||
}
|
||||
|
||||
void *MapMemory()
|
||||
|
@ -72,6 +81,7 @@ struct BufferData
|
|||
}
|
||||
void UnmapMemory()
|
||||
{
|
||||
allocation.UnmapMemory();
|
||||
}
|
||||
|
||||
vk::UniqueBuffer buffer;
|
||||
|
|
|
@ -442,7 +442,7 @@ vk::CommandBuffer TextureDrawer::BeginRenderPass()
|
|||
break;
|
||||
}
|
||||
|
||||
TSP tsp = { 0 };
|
||||
TSP tsp = { { 0 } };
|
||||
for (tsp.TexU = 0; tsp.TexU <= 7 && (8u << tsp.TexU) < origWidth; tsp.TexU++);
|
||||
for (tsp.TexV = 0; tsp.TexV <= 7 && (8u << tsp.TexV) < origHeight; tsp.TexV++);
|
||||
|
||||
|
|
|
@ -220,6 +220,10 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
|
|||
|
||||
// Upload Vertex and index Data:
|
||||
{
|
||||
if (vertex_size != 0)
|
||||
vertex_size = ((vertex_size - 1) / g_BufferMemoryAlignment + 1) * g_BufferMemoryAlignment;
|
||||
if (index_size != 0)
|
||||
index_size = ((index_size - 1) / g_BufferMemoryAlignment + 1) * g_BufferMemoryAlignment;
|
||||
ImDrawVert* vtx_dst = NULL;
|
||||
ImDrawIdx* idx_dst = NULL;
|
||||
err = vkMapMemory(g_Device, fd->VertexBufferMemory, 0, vertex_size, 0, (void**)(&vtx_dst));
|
||||
|
|
|
@ -36,7 +36,7 @@ public:
|
|||
vk::DescriptorSetLayoutBinding descSetLayoutBindings[] = {
|
||||
{ 0, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // pixel buffer
|
||||
{ 1, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // pixel counter
|
||||
{ 2, vk::DescriptorType::eStorageImage, 1, vk::ShaderStageFlagBits::eFragment }, // a-buffer pointers
|
||||
{ 2, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // a-buffer pointers
|
||||
};
|
||||
|
||||
descSetLayout = context->GetDevice().createDescriptorSetLayoutUnique(
|
||||
|
@ -62,11 +62,9 @@ public:
|
|||
}
|
||||
// We need to wait until this buffer is not used before deleting it
|
||||
context->WaitIdle();
|
||||
abufferPointerAttachment.reset();
|
||||
abufferPointerAttachment = std::unique_ptr<FramebufferAttachment>(
|
||||
new FramebufferAttachment(context->GetPhysicalDevice(), context->GetDevice()));
|
||||
abufferPointerAttachment->Init(maxWidth, maxHeight, vk::Format::eR32Uint, vk::ImageUsageFlagBits::eStorage);
|
||||
abufferPointerTransitionNeeded = true;
|
||||
abufferPointer.reset();
|
||||
abufferPointer = std::unique_ptr<BufferData>(new BufferData(maxWidth * maxHeight * sizeof(int),
|
||||
vk::BufferUsageFlagBits::eStorageBuffer, vk::MemoryPropertyFlagBits::eDeviceLocal));
|
||||
firstFrameAfterInit = true;
|
||||
|
||||
if (!descSet)
|
||||
|
@ -77,8 +75,8 @@ public:
|
|||
writeDescriptorSets.emplace_back(*descSet, 0, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &pixelBufferInfo, nullptr);
|
||||
vk::DescriptorBufferInfo pixelCounterBufferInfo(*pixelCounter->buffer, 0, 4);
|
||||
writeDescriptorSets.emplace_back(*descSet, 1, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &pixelCounterBufferInfo, nullptr);
|
||||
vk::DescriptorImageInfo pointerImageInfo(vk::Sampler(), abufferPointerAttachment->GetImageView(), vk::ImageLayout::eGeneral);
|
||||
writeDescriptorSets.emplace_back(*descSet, 2, 0, 1, vk::DescriptorType::eStorageImage, &pointerImageInfo, nullptr, nullptr);
|
||||
vk::DescriptorBufferInfo abufferPointerInfo(*abufferPointer->buffer, 0, VK_WHOLE_SIZE);
|
||||
writeDescriptorSets.emplace_back(*descSet, 2, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &abufferPointerInfo, nullptr);
|
||||
context->GetDevice().updateDescriptorSets(writeDescriptorSets, nullptr);
|
||||
}
|
||||
|
||||
|
@ -89,21 +87,7 @@ public:
|
|||
|
||||
void OnNewFrame(vk::CommandBuffer commandBuffer)
|
||||
{
|
||||
if (abufferPointerTransitionNeeded)
|
||||
{
|
||||
abufferPointerTransitionNeeded = false;
|
||||
|
||||
vk::ImageSubresourceRange imageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1);
|
||||
vk::ImageMemoryBarrier imageMemoryBarrier(vk::AccessFlags(), vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite,
|
||||
vk::ImageLayout::eUndefined, vk::ImageLayout::eGeneral, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
|
||||
abufferPointerAttachment->GetImage(), imageSubresourceRange);
|
||||
commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eFragmentShader, {}, nullptr, nullptr,
|
||||
imageMemoryBarrier);
|
||||
}
|
||||
else
|
||||
{
|
||||
firstFrameAfterInit = false;
|
||||
}
|
||||
firstFrameAfterInit = false;
|
||||
}
|
||||
|
||||
void ResetPixelCounter(vk::CommandBuffer commandBuffer)
|
||||
|
@ -117,7 +101,7 @@ public:
|
|||
pixelBuffer.reset();
|
||||
pixelCounter.reset();
|
||||
pixelCounterReset.reset();
|
||||
abufferPointerAttachment.reset();
|
||||
abufferPointer.reset();
|
||||
}
|
||||
|
||||
vk::DescriptorSetLayout GetDescriptorSetLayout() const { return *descSetLayout; }
|
||||
|
@ -130,8 +114,7 @@ private:
|
|||
std::unique_ptr<BufferData> pixelBuffer;
|
||||
std::unique_ptr<BufferData> pixelCounter;
|
||||
std::unique_ptr<BufferData> pixelCounterReset;
|
||||
std::unique_ptr<FramebufferAttachment> abufferPointerAttachment;
|
||||
bool abufferPointerTransitionNeeded = false;
|
||||
std::unique_ptr<BufferData> abufferPointer;
|
||||
bool firstFrameAfterInit = false;
|
||||
int maxWidth = 0;
|
||||
int maxHeight = 0;
|
||||
|
|
|
@ -264,6 +264,7 @@ bool OITDrawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
|
|||
fragUniforms.shade_scale_factor = FPU_SHAD_SCALE.scale_factor / 256.f;
|
||||
// sizeof(Pixel) == 16
|
||||
fragUniforms.pixelBufferSize = std::min<u64>(config::PixelBufferSize, GetContext()->GetMaxMemoryAllocationSize()) / 16;
|
||||
fragUniforms.viewportWidth = maxWidth;
|
||||
|
||||
currentScissor = vk::Rect2D();
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ public:
|
|||
float sp_FOG_DENSITY;
|
||||
float shade_scale_factor; // new for OIT
|
||||
u32 pixelBufferSize;
|
||||
u32 viewportWidth;
|
||||
};
|
||||
|
||||
struct PushConstants
|
||||
|
@ -93,8 +94,8 @@ public:
|
|||
{
|
||||
if (perFrameDescSets.empty())
|
||||
{
|
||||
perFrameDescSets = std::move(GetContext()->GetDevice().allocateDescriptorSetsUnique(
|
||||
vk::DescriptorSetAllocateInfo(GetContext()->GetDescriptorPool(), 1, &perFrameLayout)));
|
||||
perFrameDescSets = GetContext()->GetDevice().allocateDescriptorSetsUnique(
|
||||
vk::DescriptorSetAllocateInfo(GetContext()->GetDescriptorPool(), 1, &perFrameLayout));
|
||||
}
|
||||
perFrameDescSetsInFlight.emplace_back(std::move(perFrameDescSets.back()));
|
||||
perFrameDescSets.pop_back();
|
||||
|
|
|
@ -70,9 +70,12 @@ layout (std140, set = 0, binding = 1) uniform FragmentShaderUniforms
|
|||
float sp_FOG_DENSITY;
|
||||
float shade_scale_factor;
|
||||
uint pixelBufferSize;
|
||||
uint viewportWidth;
|
||||
} uniformBuffer;
|
||||
|
||||
layout(set = 3, binding = 2, r32ui) uniform coherent restrict uimage2D abufferPointerImg;
|
||||
layout(set = 3, binding = 2) buffer abufferPointer_ {
|
||||
uint pointers[];
|
||||
} abufferPointer;
|
||||
|
||||
layout(set = 3, binding = 1) buffer PixelCounter_ {
|
||||
uint buffer_index;
|
||||
|
@ -410,7 +413,7 @@ void main()
|
|||
pixel.color = packColors(clamp(color, vec4(0.0), vec4(1.0)));
|
||||
pixel.depth = gl_FragDepth;
|
||||
pixel.seq_num = uint(pushConstants.pp_Number);
|
||||
pixel.next = imageAtomicExchange(abufferPointerImg, coords, idx);
|
||||
pixel.next = atomicExchange(abufferPointer.pointers[coords.x + coords.y * uniformBuffer.viewportWidth], idx);
|
||||
PixelBuffer.pixels[idx] = pixel;
|
||||
|
||||
#endif
|
||||
|
@ -437,7 +440,7 @@ uint pixel_list[MAX_PIXELS_PER_FRAGMENT];
|
|||
int fillAndSortFragmentArray(ivec2 coords)
|
||||
{
|
||||
// Load fragments into a local memory array for sorting
|
||||
uint idx = imageLoad(abufferPointerImg, coords).x;
|
||||
uint idx = abufferPointer.pointers[coords.x + coords.y * uniformBuffer.viewportWidth];
|
||||
int count = 0;
|
||||
for (; idx != EOL && count < MAX_PIXELS_PER_FRAGMENT; count++)
|
||||
{
|
||||
|
@ -555,7 +558,7 @@ vec4 resolveAlphaBlend(ivec2 coords) {
|
|||
else
|
||||
finalColor = result;
|
||||
}
|
||||
|
||||
|
||||
return finalColor;
|
||||
|
||||
}
|
||||
|
@ -576,7 +579,7 @@ void main(void)
|
|||
ivec2 coords = ivec2(gl_FragCoord.xy);
|
||||
|
||||
// Reset pointers
|
||||
imageStore(abufferPointerImg, coords, uvec4(EOL));
|
||||
abufferPointer.pointers[coords.x + coords.y * uniformBuffer.viewportWidth] = EOL;
|
||||
}
|
||||
)";
|
||||
|
||||
|
@ -594,7 +597,7 @@ void main()
|
|||
#endif
|
||||
ivec2 coords = ivec2(gl_FragCoord.xy);
|
||||
|
||||
uint idx = imageLoad(abufferPointerImg, coords).x;
|
||||
uint idx = abufferPointer.pointers[coords.x + coords.y * uniformBuffer.viewportWidth];
|
||||
int list_len = 0;
|
||||
while (idx != EOL && list_len < MAX_PIXELS_PER_FRAGMENT)
|
||||
{
|
||||
|
|
|
@ -100,11 +100,12 @@ void VulkanOverlay::Draw(vk::CommandBuffer commandBuffer, vk::Extent2D viewport,
|
|||
f32 vmu_width = 48.f * scaling;
|
||||
|
||||
pipeline->BindPipeline(commandBuffer);
|
||||
const float *color = nullptr;
|
||||
#ifndef LIBRETRO
|
||||
vmu_height *= 2.f;
|
||||
vmu_width *= 2.f;
|
||||
float blendConstants[4] = { 0.75f, 0.75f, 0.75f, 0.75f };
|
||||
commandBuffer.setBlendConstants(blendConstants);
|
||||
color = blendConstants;
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < vmuTextures.size(); i++)
|
||||
|
@ -161,12 +162,12 @@ void VulkanOverlay::Draw(vk::CommandBuffer commandBuffer, vk::Extent2D viewport,
|
|||
commandBuffer.setViewport(0, 1, &viewport);
|
||||
commandBuffer.setScissor(0, vk::Rect2D(vk::Offset2D(x, y), vk::Extent2D(w, h)));
|
||||
|
||||
drawers[i]->Draw(commandBuffer, vmuTextures[i]->GetImageView(), vtx, true);
|
||||
drawers[i]->Draw(commandBuffer, vmuTextures[i]->GetImageView(), vtx, true, color);
|
||||
}
|
||||
}
|
||||
if (crosshair && crosshairsNeeded())
|
||||
{
|
||||
alphaPipeline->BindPipeline(commandBuffer);
|
||||
pipeline->BindPipeline(commandBuffer);
|
||||
for (size_t i = 0; i < config::CrosshairColor.size(); i++)
|
||||
{
|
||||
if (config::CrosshairColor[i] == 0)
|
||||
|
|
|
@ -35,21 +35,18 @@ public:
|
|||
void Init(QuadPipeline *pipeline)
|
||||
{
|
||||
this->pipeline = pipeline;
|
||||
alphaPipeline = std::unique_ptr<QuadPipeline>(new QuadPipeline(true));
|
||||
alphaPipeline->Init(*pipeline);
|
||||
for (auto& drawer : drawers)
|
||||
{
|
||||
drawer = std::unique_ptr<QuadDrawer>(new QuadDrawer());
|
||||
drawer->Init(pipeline);
|
||||
}
|
||||
xhairDrawer = std::unique_ptr<QuadDrawer>(new QuadDrawer());
|
||||
xhairDrawer->Init(alphaPipeline.get());
|
||||
xhairDrawer->Init(pipeline);
|
||||
}
|
||||
|
||||
void Term()
|
||||
{
|
||||
commandBuffers.clear();
|
||||
alphaPipeline.reset();
|
||||
for (auto& drawer : drawers)
|
||||
drawer.reset();
|
||||
xhairDrawer.reset();
|
||||
|
@ -67,7 +64,6 @@ private:
|
|||
std::array<std::unique_ptr<QuadDrawer>, 8> drawers;
|
||||
QuadPipeline *pipeline = nullptr;
|
||||
|
||||
std::unique_ptr<QuadPipeline> alphaPipeline;
|
||||
std::unique_ptr<Texture> xhairTexture;
|
||||
std::unique_ptr<QuadDrawer> xhairDrawer;
|
||||
};
|
||||
|
|
|
@ -46,8 +46,8 @@ public:
|
|||
{
|
||||
if (perFrameDescSets.empty())
|
||||
{
|
||||
perFrameDescSets = std::move(GetContext()->GetDevice().allocateDescriptorSetsUnique(
|
||||
vk::DescriptorSetAllocateInfo(GetContext()->GetDescriptorPool(), 1, &perFrameLayout)));
|
||||
perFrameDescSets = GetContext()->GetDevice().allocateDescriptorSetsUnique(
|
||||
vk::DescriptorSetAllocateInfo(GetContext()->GetDescriptorPool(), 1, &perFrameLayout));
|
||||
}
|
||||
perFrameDescSetsInFlight.emplace_back(std::move(perFrameDescSets.back()));
|
||||
perFrameDescSets.pop_back();
|
||||
|
|
|
@ -65,35 +65,18 @@ void QuadPipeline::CreatePipeline()
|
|||
vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo;
|
||||
|
||||
// Color flags and blending
|
||||
vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState;
|
||||
if (withAlpha)
|
||||
{
|
||||
pipelineColorBlendAttachmentState = vk::PipelineColorBlendAttachmentState(
|
||||
true, // blendEnable
|
||||
vk::BlendFactor::eSrcAlpha, // srcColorBlendFactor
|
||||
vk::BlendFactor::eOneMinusSrcAlpha, // dstColorBlendFactor
|
||||
vk::BlendOp::eAdd, // colorBlendOp
|
||||
vk::BlendFactor::eSrcAlpha, // srcAlphaBlendFactor
|
||||
vk::BlendFactor::eOneMinusSrcAlpha, // dstAlphaBlendFactor
|
||||
vk::BlendOp::eAdd, // alphaBlendOp
|
||||
vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG
|
||||
| vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
pipelineColorBlendAttachmentState = vk::PipelineColorBlendAttachmentState(
|
||||
true, // blendEnable
|
||||
vk::BlendFactor::eConstantAlpha, // srcColorBlendFactor
|
||||
vk::BlendFactor::eOneMinusConstantAlpha, // dstColorBlendFactor
|
||||
vk::BlendOp::eAdd, // colorBlendOp
|
||||
vk::BlendFactor::eConstantAlpha, // srcAlphaBlendFactor
|
||||
vk::BlendFactor::eOneMinusConstantAlpha, // dstAlphaBlendFactor
|
||||
vk::BlendOp::eAdd, // alphaBlendOp
|
||||
vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG
|
||||
| vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA
|
||||
);
|
||||
}
|
||||
vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState
|
||||
(
|
||||
true, // blendEnable
|
||||
vk::BlendFactor::eSrcAlpha, // srcColorBlendFactor
|
||||
vk::BlendFactor::eOneMinusSrcAlpha, // dstColorBlendFactor
|
||||
vk::BlendOp::eAdd, // colorBlendOp
|
||||
vk::BlendFactor::eSrcAlpha, // srcAlphaBlendFactor
|
||||
vk::BlendFactor::eOneMinusSrcAlpha, // dstAlphaBlendFactor
|
||||
vk::BlendOp::eAdd, // alphaBlendOp
|
||||
vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG
|
||||
| vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA
|
||||
);
|
||||
vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo
|
||||
(
|
||||
vk::PipelineColorBlendStateCreateFlags(), // flags
|
||||
|
@ -104,13 +87,12 @@ void QuadPipeline::CreatePipeline()
|
|||
{ { 1.0f, 1.0f, 1.0f, 1.0f } } // blendConstants
|
||||
);
|
||||
|
||||
vk::DynamicState dynamicStates[] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor, vk::DynamicState::eBlendConstants };
|
||||
vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), ARRAY_SIZE(dynamicStates) - (withAlpha ? 1 : 0),
|
||||
dynamicStates);
|
||||
vk::DynamicState dynamicStates[] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
|
||||
vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), ARRAY_SIZE(dynamicStates), dynamicStates);
|
||||
|
||||
vk::PipelineShaderStageCreateInfo stages[] = {
|
||||
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, shaderManager->GetQuadVertexShader(rotate), "main" },
|
||||
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, shaderManager->GetQuadFragmentShader(), "main" },
|
||||
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, shaderManager->GetQuadFragmentShader(ignoreTexAlpha), "main" },
|
||||
};
|
||||
vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo
|
||||
(
|
||||
|
|
|
@ -70,7 +70,8 @@ private:
|
|||
class QuadPipeline
|
||||
{
|
||||
public:
|
||||
QuadPipeline(bool withAlpha = false, bool rotate = false) : withAlpha(withAlpha), rotate(rotate) {}
|
||||
QuadPipeline(bool ignoreTexAlpha, bool rotate = false)
|
||||
: rotate(rotate), ignoreTexAlpha(ignoreTexAlpha) {}
|
||||
void Init(ShaderManager *shaderManager, vk::RenderPass renderPass);
|
||||
void Init(const QuadPipeline& other) { Init(other.shaderManager, other.renderPass); }
|
||||
void Term() {
|
||||
|
@ -102,8 +103,8 @@ private:
|
|||
vk::UniquePipelineLayout pipelineLayout;
|
||||
vk::UniqueDescriptorSetLayout descSetLayout;
|
||||
ShaderManager *shaderManager = nullptr;
|
||||
bool withAlpha;
|
||||
bool rotate;
|
||||
bool ignoreTexAlpha;
|
||||
};
|
||||
|
||||
class QuadDrawer
|
||||
|
|
|
@ -286,7 +286,12 @@ layout (location = 0) out vec4 FragColor;
|
|||
|
||||
void main()
|
||||
{
|
||||
#if IGNORE_TEX_ALPHA == 1
|
||||
FragColor.rgb = pushConstants.color.rgb * texture(tex, inUV).rgb;
|
||||
FragColor.a = pushConstants.color.a;
|
||||
#else
|
||||
FragColor = pushConstants.color * texture(tex, inUV);
|
||||
#endif
|
||||
}
|
||||
)";
|
||||
|
||||
|
@ -365,9 +370,12 @@ vk::UniqueShaderModule ShaderManager::compileQuadVertexShader(bool rotate)
|
|||
return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eVertex, src.generate());
|
||||
}
|
||||
|
||||
vk::UniqueShaderModule ShaderManager::compileQuadFragmentShader()
|
||||
vk::UniqueShaderModule ShaderManager::compileQuadFragmentShader(bool ignoreTexAlpha)
|
||||
{
|
||||
return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eFragment, VulkanSource().addSource(QuadFragmentShaderSource).generate());
|
||||
VulkanSource src;
|
||||
src.addConstant("IGNORE_TEX_ALPHA", (int)ignoreTexAlpha)
|
||||
.addSource(QuadFragmentShaderSource);
|
||||
return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eFragment,src.generate());
|
||||
}
|
||||
|
||||
vk::UniqueShaderModule ShaderManager::compileOSDVertexShader()
|
||||
|
|
|
@ -98,21 +98,30 @@ public:
|
|||
if (rotate)
|
||||
{
|
||||
if (!quadRotateVertexShader)
|
||||
quadRotateVertexShader = compileQuadVertexShader(rotate);
|
||||
quadRotateVertexShader = compileQuadVertexShader(true);
|
||||
return *quadRotateVertexShader;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!quadVertexShader)
|
||||
quadVertexShader = compileQuadVertexShader(rotate);
|
||||
quadVertexShader = compileQuadVertexShader(false);
|
||||
return *quadVertexShader;
|
||||
}
|
||||
}
|
||||
vk::ShaderModule GetQuadFragmentShader()
|
||||
vk::ShaderModule GetQuadFragmentShader(bool ignoreTexAlpha)
|
||||
{
|
||||
if (!quadFragmentShader)
|
||||
quadFragmentShader = compileQuadFragmentShader();
|
||||
return *quadFragmentShader;
|
||||
if (ignoreTexAlpha)
|
||||
{
|
||||
if (!quadNoAlphaFragmentShader)
|
||||
quadNoAlphaFragmentShader = compileQuadFragmentShader(true);
|
||||
return *quadNoAlphaFragmentShader;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!quadFragmentShader)
|
||||
quadFragmentShader = compileQuadFragmentShader(false);
|
||||
return *quadFragmentShader;
|
||||
}
|
||||
}
|
||||
vk::ShaderModule GetOSDVertexShader()
|
||||
{
|
||||
|
@ -142,7 +151,7 @@ private:
|
|||
vk::UniqueShaderModule compileModVolVertexShader();
|
||||
vk::UniqueShaderModule compileModVolFragmentShader();
|
||||
vk::UniqueShaderModule compileQuadVertexShader(bool rotate);
|
||||
vk::UniqueShaderModule compileQuadFragmentShader();
|
||||
vk::UniqueShaderModule compileQuadFragmentShader(bool ignoreTexAlpha);
|
||||
vk::UniqueShaderModule compileOSDVertexShader();
|
||||
vk::UniqueShaderModule compileOSDFragmentShader();
|
||||
|
||||
|
@ -153,6 +162,7 @@ private:
|
|||
vk::UniqueShaderModule quadVertexShader;
|
||||
vk::UniqueShaderModule quadRotateVertexShader;
|
||||
vk::UniqueShaderModule quadFragmentShader;
|
||||
vk::UniqueShaderModule quadNoAlphaFragmentShader;
|
||||
vk::UniqueShaderModule osdVertexShader;
|
||||
vk::UniqueShaderModule osdFragmentShader;
|
||||
};
|
||||
|
|
|
@ -235,8 +235,10 @@ void Texture::CreateImage(vk::ImageTiling tiling, const vk::ImageUsageFlags& usa
|
|||
image = device.createImageUnique(imageCreateInfo);
|
||||
|
||||
VmaAllocationCreateInfo allocCreateInfo = { VmaAllocationCreateFlags(), needsStaging ? VmaMemoryUsage::VMA_MEMORY_USAGE_GPU_ONLY : VmaMemoryUsage::VMA_MEMORY_USAGE_CPU_TO_GPU };
|
||||
#ifndef __APPLE__
|
||||
if (!needsStaging)
|
||||
allocCreateInfo.flags = VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||
#endif
|
||||
allocation = VulkanContext::Instance()->GetAllocator().AllocateForImage(*image, allocCreateInfo);
|
||||
|
||||
vk::ImageViewCreateInfo imageViewCreateInfo(vk::ImageViewCreateFlags(), image.get(), vk::ImageViewType::e2D, format, vk::ComponentMapping(),
|
||||
|
@ -294,6 +296,7 @@ void Texture::SetImage(u32 srcSize, void *srcData, bool isNew, bool genMipmaps)
|
|||
}
|
||||
else
|
||||
memcpy(data, srcData, srcSize);
|
||||
allocation.UnmapMemory();
|
||||
}
|
||||
else
|
||||
memcpy(data, srcData, srcSize);
|
||||
|
|
|
@ -82,7 +82,6 @@ public:
|
|||
{
|
||||
u32 samplerHash = tsp.full & TSP_Mask; // MipMapD, FilterMode, ClampU, ClampV, FlipU, FlipV
|
||||
const auto& it = samplers.find(samplerHash);
|
||||
vk::Sampler sampler;
|
||||
if (it != samplers.end())
|
||||
return it->second.get();
|
||||
vk::Filter filter = tsp.FilterMode == 0 ? vk::Filter::eNearest : vk::Filter::eLinear;
|
||||
|
@ -93,10 +92,16 @@ public:
|
|||
|
||||
bool anisotropicFiltering = config::AnisotropicFiltering > 1 && VulkanContext::Instance()->SupportsSamplerAnisotropy()
|
||||
&& filter == vk::Filter::eLinear;
|
||||
#ifndef __APPLE__
|
||||
float mipLodBias = D_Adjust_LoD_Bias[tsp.MipMapD];
|
||||
#else
|
||||
// not supported by metal
|
||||
float mipLodBias = 0;
|
||||
#endif
|
||||
return samplers.emplace(
|
||||
std::make_pair(samplerHash, VulkanContext::Instance()->GetDevice().createSamplerUnique(
|
||||
vk::SamplerCreateInfo(vk::SamplerCreateFlags(), filter, filter,
|
||||
vk::SamplerMipmapMode::eNearest, uRepeat, vRepeat, vk::SamplerAddressMode::eClampToEdge, D_Adjust_LoD_Bias[tsp.MipMapD],
|
||||
vk::SamplerMipmapMode::eNearest, uRepeat, vRepeat, vk::SamplerAddressMode::eClampToEdge, mipLodBias,
|
||||
anisotropicFiltering, std::min<float>(config::AnisotropicFiltering, VulkanContext::Instance()->GetMaxSamplerAnisotropy()),
|
||||
false, vk::CompareOp::eNever,
|
||||
0.0f, 256.0f, vk::BorderColor::eFloatOpaqueBlack)))).first->second.get();
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -27,7 +27,8 @@ VKAPI_ATTR static void VKAPI_CALL vmaAllocateDeviceMemoryCallback(
|
|||
VmaAllocator allocator,
|
||||
uint32_t memoryType,
|
||||
VkDeviceMemory memory,
|
||||
VkDeviceSize size)
|
||||
VkDeviceSize size,
|
||||
void * userData)
|
||||
{
|
||||
DEBUG_LOG(RENDERER, "VMAAllocator: %" PRIu64 " bytes allocated (type %d)", size, memoryType);
|
||||
}
|
||||
|
@ -36,7 +37,8 @@ VKAPI_ATTR static void VKAPI_CALL vmaFreeDeviceMemoryCallback(
|
|||
VmaAllocator allocator,
|
||||
uint32_t memoryType,
|
||||
VkDeviceMemory memory,
|
||||
VkDeviceSize size)
|
||||
VkDeviceSize size,
|
||||
void * userData)
|
||||
{
|
||||
DEBUG_LOG(RENDERER, "VMAAllocator: %" PRIu64 " bytes freed (type %d)", size, memoryType);
|
||||
}
|
||||
|
|
|
@ -58,8 +58,21 @@ public:
|
|||
vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &flags);
|
||||
return flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
|
||||
}
|
||||
void *MapMemory() const {
|
||||
return allocInfo.pMappedData;
|
||||
void *MapMemory() const
|
||||
{
|
||||
if (allocInfo.pMappedData != nullptr)
|
||||
return allocInfo.pMappedData;
|
||||
void *p;
|
||||
vmaMapMemory(allocator, allocation, &p);
|
||||
return p;
|
||||
}
|
||||
void UnmapMemory() const
|
||||
{
|
||||
if (allocInfo.pMappedData != nullptr)
|
||||
return;
|
||||
// Only needed (and useful) for non-host coherent memory
|
||||
vmaFlushAllocation(allocator, allocation, allocInfo.offset, allocInfo.size);
|
||||
vmaUnmapMemory(allocator, allocation);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -151,9 +151,10 @@ bool VulkanContext::InitInstance(const char** extensions, uint32_t extensions_co
|
|||
#ifdef VK_DEBUG
|
||||
#ifndef __ANDROID__
|
||||
vext.push_back("VK_EXT_debug_utils");
|
||||
vext.push_back("VK_EXT_debug_report");
|
||||
// layer_names.push_back("VK_LAYER_KHRONOS_validation");
|
||||
layer_names.push_back("VK_LAYER_LUNARG_standard_validation");
|
||||
layer_names.push_back("VK_LAYER_LUNARG_assistant_layer");
|
||||
// layer_names.push_back("VK_LAYER_LUNARG_standard_validation");
|
||||
// layer_names.push_back("VK_LAYER_LUNARG_assistant_layer");
|
||||
#else
|
||||
vext.push_back("VK_EXT_debug_report"); // NDK <= 19?
|
||||
layer_names.push_back("VK_LAYER_GOOGLE_threading");
|
||||
|
@ -375,13 +376,23 @@ bool VulkanContext::InitDevice()
|
|||
deviceExtensions.push_back(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
|
||||
dedicatedAllocationSupported = true;
|
||||
}
|
||||
else if (!strcmp(property.extensionName, "VK_KHR_portability_subset"))
|
||||
deviceExtensions.push_back("VK_KHR_portability_subset");
|
||||
#ifdef VK_DEBUG
|
||||
else if (!strcmp(property.extensionName, VK_EXT_DEBUG_MARKER_EXTENSION_NAME)
|
||||
|| !strcmp(property.extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME)
|
||||
|| !strcmp(property.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME))
|
||||
else if (!strcmp(property.extensionName, VK_EXT_DEBUG_MARKER_EXTENSION_NAME))
|
||||
{
|
||||
NOTICE_LOG(RENDERER, "Debug extension %s available", property.extensionName);
|
||||
deviceExtensions.push_back(property.extensionName);
|
||||
deviceExtensions.push_back(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
|
||||
}
|
||||
else if(!strcmp(property.extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME))
|
||||
{
|
||||
NOTICE_LOG(RENDERER, "Debug extension %s available", property.extensionName);
|
||||
deviceExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
|
||||
}
|
||||
else if (!strcmp(property.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME))
|
||||
{
|
||||
NOTICE_LOG(RENDERER, "Debug extension %s available", property.extensionName);
|
||||
deviceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -445,9 +456,10 @@ bool VulkanContext::InitDevice()
|
|||
allocator.Init(physicalDevice, *device);
|
||||
|
||||
shaderManager = std::unique_ptr<ShaderManager>(new ShaderManager());
|
||||
quadPipeline = std::unique_ptr<QuadPipeline>(new QuadPipeline());
|
||||
quadPipeline = std::unique_ptr<QuadPipeline>(new QuadPipeline(true, false));
|
||||
quadPipelineWithAlpha = std::unique_ptr<QuadPipeline>(new QuadPipeline(false, false));
|
||||
quadDrawer = std::unique_ptr<QuadDrawer>(new QuadDrawer());
|
||||
quadRotatePipeline = std::unique_ptr<QuadPipeline>(new QuadPipeline(false, true));
|
||||
quadRotatePipeline = std::unique_ptr<QuadPipeline>(new QuadPipeline(true, true));
|
||||
quadRotateDrawer = std::unique_ptr<QuadDrawer>(new QuadDrawer());
|
||||
|
||||
CreateSwapChain();
|
||||
|
@ -548,10 +560,6 @@ void VulkanContext::CreateSwapChain()
|
|||
|
||||
vk::SurfaceTransformFlagBitsKHR preTransform = (surfaceCapabilities.supportedTransforms & vk::SurfaceTransformFlagBitsKHR::eIdentity) ? vk::SurfaceTransformFlagBitsKHR::eIdentity : surfaceCapabilities.currentTransform;
|
||||
|
||||
vk::CompositeAlphaFlagBitsKHR compositeAlpha =
|
||||
(surfaceCapabilities.supportedCompositeAlpha & vk::CompositeAlphaFlagBitsKHR::ePreMultiplied) ? vk::CompositeAlphaFlagBitsKHR::ePreMultiplied :
|
||||
(surfaceCapabilities.supportedCompositeAlpha & vk::CompositeAlphaFlagBitsKHR::ePostMultiplied) ? vk::CompositeAlphaFlagBitsKHR::ePostMultiplied :
|
||||
(surfaceCapabilities.supportedCompositeAlpha & vk::CompositeAlphaFlagBitsKHR::eInherit) ? vk::CompositeAlphaFlagBitsKHR::eInherit : vk::CompositeAlphaFlagBitsKHR::eOpaque;
|
||||
u32 imageCount = std::max(3u, surfaceCapabilities.minImageCount);
|
||||
if (surfaceCapabilities.maxImageCount != 0)
|
||||
imageCount = std::min(imageCount, surfaceCapabilities.maxImageCount);
|
||||
|
@ -561,7 +569,7 @@ void VulkanContext::CreateSwapChain()
|
|||
usage |= vk::ImageUsageFlagBits::eTransferSrc;
|
||||
#endif
|
||||
vk::SwapchainCreateInfoKHR swapChainCreateInfo(vk::SwapchainCreateFlagsKHR(), GetSurface(), imageCount, colorFormat, vk::ColorSpaceKHR::eSrgbNonlinear,
|
||||
swapchainExtent, 1, usage, vk::SharingMode::eExclusive, 0, nullptr, preTransform, compositeAlpha, swapchainPresentMode, true, nullptr);
|
||||
swapchainExtent, 1, usage, vk::SharingMode::eExclusive, 0, nullptr, preTransform, vk::CompositeAlphaFlagBitsKHR::eOpaque, swapchainPresentMode, true, nullptr);
|
||||
|
||||
u32 queueFamilyIndices[2] = { graphicsQueueIndex, presentQueueIndex };
|
||||
if (graphicsQueueIndex != presentQueueIndex)
|
||||
|
@ -635,10 +643,11 @@ void VulkanContext::CreateSwapChain()
|
|||
imageAcquiredSemaphores.push_back(device->createSemaphoreUnique(vk::SemaphoreCreateInfo()));
|
||||
}
|
||||
quadPipeline->Init(shaderManager.get(), *renderPass);
|
||||
quadPipelineWithAlpha->Init(shaderManager.get(), *renderPass);
|
||||
quadDrawer->Init(quadPipeline.get());
|
||||
quadRotatePipeline->Init(shaderManager.get(), *renderPass);
|
||||
quadRotateDrawer->Init(quadRotatePipeline.get());
|
||||
overlay->Init(quadPipeline.get());
|
||||
overlay->Init(quadPipelineWithAlpha.get());
|
||||
|
||||
InitImgui();
|
||||
|
||||
|
@ -664,8 +673,8 @@ bool VulkanContext::Init()
|
|||
return false;
|
||||
uint32_t extensionsCount = 0;
|
||||
SDL_Vulkan_GetInstanceExtensions((SDL_Window *)window, &extensionsCount, NULL);
|
||||
extensions.resize(extensionsCount + 1);
|
||||
SDL_Vulkan_GetInstanceExtensions((SDL_Window *)window, &extensionsCount, &extensions[1]);
|
||||
extensions.resize(extensionsCount + extensions.size());
|
||||
SDL_Vulkan_GetInstanceExtensions((SDL_Window *)window, &extensionsCount, &extensions[extensions.size() - extensionsCount]);
|
||||
#elif defined(_WIN32)
|
||||
extern void CreateMainWindow();
|
||||
CreateMainWindow();
|
||||
|
@ -685,6 +694,14 @@ bool VulkanContext::Init()
|
|||
if (SDL_Vulkan_CreateSurface((SDL_Window *)window, (VkInstance)*instance, &surface) == 0)
|
||||
return false;
|
||||
this->surface.reset(vk::SurfaceKHR(surface));
|
||||
SDL_Window *sdlWin = (SDL_Window *)window;
|
||||
int w, h;
|
||||
SDL_GetWindowSize(sdlWin, &w, &h);
|
||||
SDL_Vulkan_GetDrawableSize(sdlWin, &settings.display.width, &settings.display.height);
|
||||
settings.display.pointScale = (float)settings.display.width / w;
|
||||
float hdpi, vdpi;
|
||||
if (!SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(sdlWin), nullptr, &hdpi, &vdpi))
|
||||
screen_dpi = (int)roundf(std::max(hdpi, vdpi));
|
||||
#elif defined(_WIN32)
|
||||
vk::Win32SurfaceCreateInfoKHR createInfo(vk::Win32SurfaceCreateFlagsKHR(), GetModuleHandle(NULL), (HWND)window);
|
||||
surface = instance->createWin32SurfaceKHRUnique(createInfo);
|
||||
|
@ -694,6 +711,11 @@ bool VulkanContext::Init()
|
|||
#elif defined(__ANDROID__)
|
||||
vk::AndroidSurfaceCreateInfoKHR createInfo(vk::AndroidSurfaceCreateFlagsKHR(), (struct ANativeWindow*)window);
|
||||
surface = instance->createAndroidSurfaceKHRUnique(createInfo);
|
||||
#elif defined(__APPLE__)
|
||||
vk::MacOSSurfaceCreateInfoMVK createInfo(vk::MacOSSurfaceCreateFlagsMVK(), window);
|
||||
surface = instance->createMacOSSurfaceMVKUnique(createInfo);
|
||||
#else
|
||||
#error "Unknown Vulkan platform"
|
||||
#endif
|
||||
overlay = std::unique_ptr<VulkanOverlay>(new VulkanOverlay());
|
||||
|
||||
|
@ -792,9 +814,6 @@ void VulkanContext::DrawFrame(vk::ImageView imageView, const vk::Extent2D& exten
|
|||
else
|
||||
quadPipeline->BindPipeline(commandBuffer);
|
||||
|
||||
float blendConstants[4] = { 1.0, 1.0, 1.0, 1.0 };
|
||||
commandBuffer.setBlendConstants(blendConstants);
|
||||
|
||||
float marginWidth;
|
||||
if (config::Rotate90)
|
||||
marginWidth = ((float)width - (float)extent.height / extent.width * height) / 2.f;
|
||||
|
@ -802,7 +821,7 @@ void VulkanContext::DrawFrame(vk::ImageView imageView, const vk::Extent2D& exten
|
|||
marginWidth = ((float)width - (float)extent.width / extent.height * height) / 2.f;
|
||||
vk::Viewport viewport(marginWidth, 0, width - marginWidth * 2.f, height);
|
||||
commandBuffer.setViewport(0, 1, &viewport);
|
||||
commandBuffer.setScissor(0, vk::Rect2D(vk::Offset2D(marginWidth, 0), vk::Extent2D(width - marginWidth * 2.f, height)));
|
||||
commandBuffer.setScissor(0, vk::Rect2D(vk::Offset2D(std::max(0.f, marginWidth), 0), vk::Extent2D(width - marginWidth * 2.f, height)));
|
||||
if (config::Rotate90)
|
||||
quadRotateDrawer->Draw(commandBuffer, imageView, vtx);
|
||||
else
|
||||
|
@ -907,6 +926,7 @@ void VulkanContext::Term()
|
|||
renderPass.reset();
|
||||
quadDrawer.reset();
|
||||
quadPipeline.reset();
|
||||
quadPipelineWithAlpha.reset();
|
||||
quadRotateDrawer.reset();
|
||||
quadRotatePipeline.reset();
|
||||
shaderManager.reset();
|
||||
|
|
|
@ -200,6 +200,7 @@ private:
|
|||
|
||||
vk::UniquePipelineCache pipelineCache;
|
||||
|
||||
std::unique_ptr<QuadPipeline> quadPipelineWithAlpha;
|
||||
std::unique_ptr<QuadPipeline> quadPipeline;
|
||||
std::unique_ptr<QuadPipeline> quadRotatePipeline;
|
||||
std::unique_ptr<QuadDrawer> quadDrawer;
|
||||
|
|
|
@ -77,6 +77,7 @@ protected:
|
|||
public:
|
||||
void Term() override
|
||||
{
|
||||
GetContext()->WaitIdle();
|
||||
GetContext()->PresentFrame(nullptr, nullptr, vk::Extent2D());
|
||||
#ifdef LIBRETRO
|
||||
overlay->Term();
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
#include "sdl/sdl.h"
|
||||
#include <SDL_syswm.h>
|
||||
#include <SDL_video.h>
|
||||
#ifdef USE_VULKAN
|
||||
#include <SDL_vulkan.h>
|
||||
#endif
|
||||
#endif
|
||||
#include "hw/maple/maple_devs.h"
|
||||
#include "sdl_gamepad.h"
|
||||
|
@ -36,8 +39,7 @@ static std::shared_ptr<SDLMouse> sdl_mouse;
|
|||
static std::shared_ptr<SDLKeyboardDevice> sdl_keyboard;
|
||||
static bool window_fullscreen;
|
||||
static bool window_maximized;
|
||||
static int window_width = WINDOW_WIDTH;
|
||||
static int window_height = WINDOW_HEIGHT;
|
||||
static SDL_Rect windowPos { SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT };
|
||||
static bool gameRunning;
|
||||
static bool mouseCaptured;
|
||||
static std::string clipboardText;
|
||||
|
@ -177,14 +179,12 @@ void input_sdl_init()
|
|||
if (SDL_WasInit(SDL_INIT_HAPTIC) == 0)
|
||||
SDL_InitSubSystem(SDL_INIT_HAPTIC);
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
SDL_SetRelativeMouseMode(SDL_FALSE);
|
||||
|
||||
EventManager::listen(Event::Pause, emuEventCallback);
|
||||
EventManager::listen(Event::Resume, emuEventCallback);
|
||||
|
||||
checkRawInput();
|
||||
#endif
|
||||
|
||||
#ifdef __SWITCH__
|
||||
// when railed, both joycons are mapped to joystick #0,
|
||||
|
@ -211,7 +211,6 @@ void input_sdl_handle()
|
|||
{
|
||||
switch (event.type)
|
||||
{
|
||||
#if !defined(__APPLE__)
|
||||
case SDL_QUIT:
|
||||
dc_exit();
|
||||
break;
|
||||
|
@ -274,7 +273,7 @@ void input_sdl_handle()
|
|||
SDL_ShowCursor(SDL_ENABLE);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case SDL_JOYBUTTONDOWN:
|
||||
case SDL_JOYBUTTONUP:
|
||||
{
|
||||
|
@ -330,7 +329,6 @@ void input_sdl_handle()
|
|||
}
|
||||
break;
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
case SDL_MOUSEMOTION:
|
||||
gui_set_mouse_position(event.motion.x, event.motion.y);
|
||||
checkRawInput();
|
||||
|
@ -393,7 +391,7 @@ void input_sdl_handle()
|
|||
if (!config::UseRawInput)
|
||||
sdl_mouse->setWheel(-event.wheel.y);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case SDL_JOYDEVICEADDED:
|
||||
sdl_open_joystick(event.jdevice.which);
|
||||
break;
|
||||
|
@ -411,8 +409,6 @@ void sdl_window_set_text(const char* text)
|
|||
SDL_SetWindowTitle(window, text);
|
||||
}
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
|
||||
static float hdpiScaling = 1.f;
|
||||
|
||||
static inline void get_window_state()
|
||||
|
@ -421,9 +417,10 @@ static inline void get_window_state()
|
|||
window_fullscreen = flags & SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
window_maximized = flags & SDL_WINDOW_MAXIMIZED;
|
||||
if (!window_fullscreen && !window_maximized){
|
||||
SDL_GetWindowSize(window, &window_width, &window_height);
|
||||
window_width /= hdpiScaling;
|
||||
window_height /= hdpiScaling;
|
||||
SDL_GetWindowSize(window, &windowPos.w, &windowPos.h);
|
||||
windowPos.w /= hdpiScaling;
|
||||
windowPos.h /= hdpiScaling;
|
||||
SDL_GetWindowPosition(window, &windowPos.x, &windowPos.y);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -464,41 +461,39 @@ bool sdl_recreate_window(u32 flags)
|
|||
hdpiScaling = scaling;
|
||||
}
|
||||
}
|
||||
SDL_UnloadObject(shcoreDLL);
|
||||
}
|
||||
#endif
|
||||
|
||||
int x = SDL_WINDOWPOS_UNDEFINED;
|
||||
int y = SDL_WINDOWPOS_UNDEFINED;
|
||||
#ifdef __SWITCH__
|
||||
AppletOperationMode om = appletGetOperationMode();
|
||||
if (om == AppletOperationMode_Handheld)
|
||||
{
|
||||
window_width = 1280;
|
||||
window_height = 720;
|
||||
windowPos.w = 1280;
|
||||
windowPos.h = 720;
|
||||
scaling = 1.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
window_width = 1920;
|
||||
window_height = 1080;
|
||||
windowPos.w = 1920;
|
||||
windowPos.h = 1080;
|
||||
scaling = 1.0f;
|
||||
}
|
||||
#else
|
||||
window_width = cfgLoadInt("window", "width", window_width);
|
||||
window_height = cfgLoadInt("window", "height", window_height);
|
||||
windowPos.x = cfgLoadInt("window", "left", windowPos.x);
|
||||
windowPos.y = cfgLoadInt("window", "top", windowPos.y);
|
||||
windowPos.w = cfgLoadInt("window", "width", windowPos.w);
|
||||
windowPos.h = cfgLoadInt("window", "height", windowPos.h);
|
||||
window_fullscreen = cfgLoadBool("window", "fullscreen", window_fullscreen);
|
||||
window_maximized = cfgLoadBool("window", "maximized", window_maximized);
|
||||
if (window != nullptr)
|
||||
{
|
||||
SDL_GetWindowPosition(window, &x, &y);
|
||||
get_window_state();
|
||||
}
|
||||
#endif
|
||||
if (window != nullptr)
|
||||
SDL_DestroyWindow(window);
|
||||
|
||||
#if !defined(GLES)
|
||||
flags |= SDL_WINDOW_RESIZABLE;
|
||||
flags |= SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
if (window_fullscreen)
|
||||
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
else if (window_maximized)
|
||||
|
@ -507,16 +502,17 @@ bool sdl_recreate_window(u32 flags)
|
|||
flags |= SDL_WINDOW_FULLSCREEN;
|
||||
#endif
|
||||
|
||||
window = SDL_CreateWindow("Flycast", x, y, window_width * hdpiScaling, window_height * hdpiScaling, flags);
|
||||
window = SDL_CreateWindow("Flycast", windowPos.x, windowPos.y,
|
||||
windowPos.w * hdpiScaling, windowPos.h * hdpiScaling, flags);
|
||||
if (window == nullptr)
|
||||
{
|
||||
ERROR_LOG(COMMON, "Window creation failed: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
settings.display.width = window_width * hdpiScaling;
|
||||
settings.display.height = window_height * hdpiScaling;
|
||||
settings.display.width = windowPos.w * hdpiScaling;
|
||||
settings.display.height = windowPos.h * hdpiScaling;
|
||||
|
||||
#if !defined(GLES) && !defined(_WIN32) && !defined(__SWITCH__)
|
||||
#if !defined(GLES) && !defined(_WIN32) && !defined(__SWITCH__) && !defined(__APPLE__)
|
||||
// Set the window icon
|
||||
u32 pixels[48 * 48];
|
||||
for (int i = 0; i < 48 * 48; i++)
|
||||
|
@ -567,6 +563,9 @@ void sdl_window_create()
|
|||
{
|
||||
die("error initializing SDL Video subsystem");
|
||||
}
|
||||
#if defined(__APPLE__) && defined(USE_VULKAN)
|
||||
SDL_Vulkan_LoadLibrary("libvulkan.dylib");
|
||||
#endif
|
||||
}
|
||||
InitRenderApi();
|
||||
// ImGui copy & paste
|
||||
|
@ -578,13 +577,13 @@ void sdl_window_destroy()
|
|||
{
|
||||
#ifndef __SWITCH__
|
||||
get_window_state();
|
||||
cfgSaveInt("window", "width", window_width);
|
||||
cfgSaveInt("window", "height", window_height);
|
||||
cfgSaveInt("window", "left", windowPos.x);
|
||||
cfgSaveInt("window", "top", windowPos.y);
|
||||
cfgSaveInt("window", "width", windowPos.w);
|
||||
cfgSaveInt("window", "height", windowPos.h);
|
||||
cfgSaveBool("window", "maximized", window_maximized);
|
||||
cfgSaveBool("window", "fullscreen", window_fullscreen);
|
||||
#endif
|
||||
TermRenderApi();
|
||||
SDL_DestroyWindow(window);
|
||||
}
|
||||
|
||||
#endif // !defined(__APPLE__)
|
||||
|
|
|
@ -49,7 +49,7 @@ private:
|
|||
|
||||
#include "libretro.h"
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#elif defined(TARGET_IPHONE)
|
||||
|
||||
#include "osx.h"
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#if defined(__APPLE__) && !defined(LIBRETRO)
|
||||
#if defined(TARGET_IPHONE) && !defined(LIBRETRO)
|
||||
#include "gl_context.h"
|
||||
|
||||
OSXGraphicsContext theGLContext;
|
||||
|
|
|
@ -19,13 +19,9 @@
|
|||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#if defined(__APPLE__)
|
||||
#if defined(TARGET_IPHONE) //apple-specific ogles3 headers
|
||||
#include <OpenGLES/ES3/gl.h>
|
||||
#include <OpenGLES/ES3/glext.h>
|
||||
#else
|
||||
#include <OpenGL/gl3.h>
|
||||
#endif
|
||||
#include "gl_context.h"
|
||||
|
||||
class OSXGraphicsContext : public GLGraphicsContext
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#if defined(USE_SDL) && !defined(__APPLE__)
|
||||
#if defined(USE_SDL)
|
||||
#include <math.h>
|
||||
#include <algorithm>
|
||||
#include "gl_context.h"
|
||||
|
@ -43,8 +43,6 @@ bool SDLGLGraphicsContext::Init()
|
|||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
|
||||
if (!sdl_recreate_window(SDL_WINDOW_OPENGL))
|
||||
|
@ -54,9 +52,14 @@ bool SDLGLGraphicsContext::Init()
|
|||
#ifndef GLES
|
||||
if (glcontext == SDL_GLContext())
|
||||
{
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
#ifdef __APPLE__
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||
#else
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
#endif
|
||||
glcontext = SDL_GL_CreateContext(window);
|
||||
}
|
||||
#endif
|
||||
|
@ -69,10 +72,13 @@ bool SDLGLGraphicsContext::Init()
|
|||
}
|
||||
SDL_GL_MakeCurrent(window, NULL);
|
||||
|
||||
int w, h;
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
SDL_GL_GetDrawableSize(window, &settings.display.width, &settings.display.height);
|
||||
settings.display.pointScale = (float)settings.display.width / w;
|
||||
|
||||
float ddpi, hdpi, vdpi;
|
||||
if (!SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(window), &ddpi, &hdpi, &vdpi))
|
||||
float hdpi, vdpi;
|
||||
if (!SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(window), nullptr, &hdpi, &vdpi))
|
||||
screen_dpi = (int)roundf(std::max(hdpi, vdpi));
|
||||
|
||||
INFO_LOG(RENDERER, "Created SDL Window and GL Context successfully");
|
||||
|
@ -98,7 +104,7 @@ bool SDLGLGraphicsContext::Init()
|
|||
|
||||
#ifdef GLES
|
||||
load_gles_symbols();
|
||||
#else
|
||||
#elif !defined(__APPLE__)
|
||||
if (gl3wInit() == -1 || !gl3wIsSupported(3, 0))
|
||||
{
|
||||
ERROR_LOG(RENDERER, "gl3wInit failed or GL 3.0 not supported");
|
||||
|
|
|
@ -21,7 +21,9 @@
|
|||
#pragma once
|
||||
#include <SDL.h>
|
||||
#include "types.h"
|
||||
#ifndef GLES
|
||||
#if defined(__APPLE__) && !defined(TARGET_IPHONE)
|
||||
#include <OpenGL/gl3.h>
|
||||
#elif !defined(GLES)
|
||||
#include <GL4/gl3w.h>
|
||||
#else
|
||||
#include <GLES32/gl32.h>
|
||||
|
|
|
@ -40,5 +40,7 @@
|
|||
<string>Flycast requires microphone access to emulate the Dreamcast microphone</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
//
|
||||
// AppDelegate.swift
|
||||
// emulator-osx
|
||||
//
|
||||
// Created by admin on 6/1/15.
|
||||
// Copyright (c) 2015 reicast. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
@NSApplicationMain
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
@IBOutlet weak var window: NSWindow!
|
||||
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
if let name = Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String {
|
||||
window.title = name
|
||||
}
|
||||
NSApplication.shared.mainMenu?.item(at: 1)?.submenu?.insertItem(
|
||||
NSMenuItem(title: "New Instance", action: #selector(self.newInstance(_:)), keyEquivalent: "n"), at: 0
|
||||
)
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
emu_flycast_term()
|
||||
}
|
||||
|
||||
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func applicationDockMenu(_ sender: NSApplication) -> NSMenu? {
|
||||
let dockMenu = NSMenu()
|
||||
dockMenu.addItem(withTitle: "New Instance", action: #selector(self.newInstance(_:)), keyEquivalent: "n")
|
||||
return dockMenu
|
||||
}
|
||||
|
||||
@objc func newInstance(_ sender: NSMenuItem) {
|
||||
Process.launchedProcess(launchPath: "/usr/bin/open", arguments: ["-n", Bundle.main.bundlePath])
|
||||
}
|
||||
}
|
||||
|
|
@ -1,190 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13771"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||
<connections>
|
||||
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Flycast" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
|
||||
<items>
|
||||
<menuItem title="Flycast" id="1Xt-HY-uBw">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Flycast" systemMenu="apple" id="uQy-DD-JDr">
|
||||
<items>
|
||||
<menuItem title="About Flycast" id="5kV-Vb-QxS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
|
||||
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
|
||||
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
|
||||
<menuItem title="Services" id="NMo-om-nkz">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
|
||||
<menuItem title="Hide Flycast" keyEquivalent="h" id="Olw-nP-bQN">
|
||||
<connections>
|
||||
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Show All" id="Kd2-mp-pUS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
|
||||
<menuItem title="Quit" keyEquivalent="q" id="4sb-4s-VLi">
|
||||
<connections>
|
||||
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="File" id="dMs-cI-mzQ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="File" id="bib-Uj-vzu">
|
||||
<items>
|
||||
<menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
|
||||
<connections>
|
||||
<action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Open Recent" id="tXI-mr-wws">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
|
||||
<items>
|
||||
<menuItem title="Clear Menu" id="vNY-rz-j42">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
|
||||
<menuItem title="Open Menu" id="XKM-mk-ga2" userLabel="Open Menu">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="openMenu:" target="-1" id="OAx-Nf-cVv"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="GMh-rs-hWH"/>
|
||||
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
|
||||
<connections>
|
||||
<action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Edit" id="5QF-Oa-p0T">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
|
||||
<items>
|
||||
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
|
||||
<connections>
|
||||
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
|
||||
<connections>
|
||||
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
|
||||
<connections>
|
||||
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Delete" id="pa3-QI-u2k">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
|
||||
<connections>
|
||||
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Window" id="aUF-d1-5bR">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
|
||||
<items>
|
||||
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
|
||||
<connections>
|
||||
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Zoom" id="R4o-n2-Eq4">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
|
||||
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Help" id="wpr-3q-Mcd">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
|
||||
<items>
|
||||
<menuItem title="Flycast Help" keyEquivalent="?" id="FKE-Sm-Kum">
|
||||
<connections>
|
||||
<action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
<window title="Flycast" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="335" y="390" width="960" height="720"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
||||
<view key="contentView" id="EiT-Mj-1SZ" customClass="EmuGLView" customModule="Flycast" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="960" height="720"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</view>
|
||||
<point key="canvasLocation" x="525" y="479"/>
|
||||
</window>
|
||||
</objects>
|
||||
</document>
|
|
@ -1,171 +0,0 @@
|
|||
//
|
||||
// EmuGLView.swift
|
||||
// emulator-osx
|
||||
//
|
||||
// Created by admin on 8/5/15.
|
||||
// Copyright (c) 2015 reicast. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
class EmuGLView: NSOpenGLView, NSWindowDelegate {
|
||||
|
||||
var backingRect: NSRect?
|
||||
var swapOnVSync = emu_vsync_enabled()
|
||||
|
||||
override var acceptsFirstResponder: Bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
override func draw(_ dirtyRect: NSRect) {
|
||||
super.draw(dirtyRect)
|
||||
backingRect = convertToBacking(dirtyRect)
|
||||
|
||||
if swapOnVSync {
|
||||
draw()
|
||||
}
|
||||
}
|
||||
|
||||
func draw() {
|
||||
if swapOnVSync == (emu_fast_forward() || !emu_vsync_enabled()) {
|
||||
swapOnVSync = (!emu_fast_forward() && emu_vsync_enabled())
|
||||
var sync: GLint = swapOnVSync ? 1 : 0
|
||||
CGLSetParameter(openGLContext!.cglContextObj!, kCGLCPSwapInterval, &sync)
|
||||
}
|
||||
|
||||
if let backingRect = backingRect {
|
||||
openGLContext!.makeCurrentContext()
|
||||
if emu_single_frame(Int32(backingRect.width), Int32(backingRect.height)) {
|
||||
openGLContext!.flushBuffer() //Swap for macOS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func awakeFromNib() {
|
||||
//self.wantsBestResolutionOpenGLSurface = true
|
||||
let renderTimer = Timer.scheduledTimer(timeInterval: 0.001, target: self, selector: #selector(EmuGLView.timerTick), userInfo: nil, repeats: true)
|
||||
|
||||
RunLoop.current.add(renderTimer, forMode: .default)
|
||||
RunLoop.current.add(renderTimer, forMode: .eventTracking)
|
||||
|
||||
let attrs:[NSOpenGLPixelFormatAttribute] =
|
||||
[
|
||||
UInt32(NSOpenGLPFADoubleBuffer),
|
||||
UInt32(NSOpenGLPFADepthSize), UInt32(24),
|
||||
UInt32(NSOpenGLPFAStencilSize), UInt32(8),
|
||||
// Must specify the 3.2 Core Profile to use OpenGL 3.2
|
||||
UInt32(NSOpenGLPFAOpenGLProfile),
|
||||
UInt32(NSOpenGLProfileVersion3_2Core),
|
||||
UInt32(NSOpenGLPFABackingStore), UInt32(truncating: true),
|
||||
UInt32(0)
|
||||
]
|
||||
|
||||
let pf = NSOpenGLPixelFormat(attributes:attrs)
|
||||
|
||||
let context = NSOpenGLContext(format: pf!, share: nil)
|
||||
|
||||
self.pixelFormat = pf
|
||||
self.openGLContext = context
|
||||
|
||||
openGLContext!.makeCurrentContext()
|
||||
let rect = convertToBacking(frame)
|
||||
emu_gles_init(Int32(rect.width), Int32(rect.height))
|
||||
|
||||
if (emu_flycast_init() != 0) {
|
||||
let alert = NSAlert()
|
||||
alert.alertStyle = .critical
|
||||
alert.messageText = "Flycast initialization failed"
|
||||
alert.runModal()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@objc func timerTick() {
|
||||
if (!emu_renderer_enabled()) {
|
||||
NSApplication.shared.terminate(self)
|
||||
}
|
||||
else if emu_frame_pending() {
|
||||
if swapOnVSync {
|
||||
self.needsDisplay = true
|
||||
} else {
|
||||
self.draw()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func keyDown(with e: NSEvent) {
|
||||
if (!e.isARepeat)
|
||||
{
|
||||
emu_key_input(e.keyCode, true, UInt32(e.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue))
|
||||
}
|
||||
emu_character_input(e.characters)
|
||||
}
|
||||
|
||||
override func keyUp(with e: NSEvent) {
|
||||
emu_key_input(e.keyCode, false, UInt32(e.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue))
|
||||
}
|
||||
|
||||
override func flagsChanged(with e: NSEvent) {
|
||||
emu_key_input(0xFF, false, UInt32(e.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue))
|
||||
}
|
||||
|
||||
private func setMousePos(_ event: NSEvent)
|
||||
{
|
||||
let point = convertToBacking(convert(event.locationInWindow, from: self))
|
||||
let size = convertToBacking(frame.size)
|
||||
emu_set_mouse_position(Int32(point.x), Int32(size.height - point.y), Int32(size.width), Int32(size.height))
|
||||
}
|
||||
override func mouseDown(with event: NSEvent) {
|
||||
emu_mouse_buttons(1, true)
|
||||
setMousePos(event)
|
||||
}
|
||||
override func mouseUp(with event: NSEvent) {
|
||||
emu_mouse_buttons(1, false)
|
||||
setMousePos(event)
|
||||
}
|
||||
override func rightMouseDown(with event: NSEvent) {
|
||||
emu_mouse_buttons(2, true)
|
||||
setMousePos(event)
|
||||
}
|
||||
override func rightMouseUp(with event: NSEvent) {
|
||||
emu_mouse_buttons(2, false)
|
||||
setMousePos(event)
|
||||
}
|
||||
// Not dispatched by default. Need to set Window.acceptsMouseMovedEvents to true
|
||||
override func mouseMoved(with event: NSEvent) {
|
||||
setMousePos(event)
|
||||
}
|
||||
override func mouseDragged(with event: NSEvent) {
|
||||
emu_mouse_buttons(1, true)
|
||||
setMousePos(event)
|
||||
}
|
||||
override func rightMouseDragged(with event: NSEvent) {
|
||||
emu_mouse_buttons(2, true)
|
||||
setMousePos(event)
|
||||
}
|
||||
override func otherMouseDown(with event: NSEvent) {
|
||||
emu_mouse_buttons(3, true)
|
||||
setMousePos(event)
|
||||
}
|
||||
override func otherMouseUp(with event: NSEvent) {
|
||||
emu_mouse_buttons(3, false)
|
||||
setMousePos(event)
|
||||
}
|
||||
override func scrollWheel(with event: NSEvent) {
|
||||
if (event.hasPreciseScrollingDeltas) {
|
||||
emu_mouse_wheel(-Float(event.scrollingDeltaY) / 5)
|
||||
} else {
|
||||
emu_mouse_wheel(-Float(event.scrollingDeltaY) * 10)
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidMoveToWindow() {
|
||||
super.viewDidMoveToWindow()
|
||||
self.window!.delegate = self
|
||||
self.window!.acceptsMouseMovedEvents = true
|
||||
}
|
||||
|
||||
@IBAction func openMenu(_ sender: Any) {
|
||||
emu_gui_open_settings();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
/* SDLMain.m - main entry point for our Cocoa-ized SDL app
|
||||
Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
|
||||
Non-NIB-Code & other changes: Max Horn <max@quendi.de>
|
||||
Feel free to customize this file to suit your needs
|
||||
*/
|
||||
|
||||
#ifndef _SDLMain_h_
|
||||
#define _SDLMain_h_
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface SDLMain : NSObject
|
||||
@end
|
||||
|
||||
#endif /* _SDLMain_h_ */
|
||||
|
|
@ -0,0 +1,459 @@
|
|||
/* SDLMain.m - main entry point for our Cocoa-ized SDL app
|
||||
Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
|
||||
Non-NIB-Code & other changes: Max Horn <max@quendi.de>
|
||||
|
||||
Feel free to customize this file to suit your needs
|
||||
*/
|
||||
#include <SDL.h>
|
||||
#include "SDLMain.h"
|
||||
#include <sys/param.h> /* for MAXPATHLEN */
|
||||
#include <unistd.h>
|
||||
#include "rend/gui.h"
|
||||
|
||||
/* For some reaon, Apple removed setAppleMenu from the headers in 10.4,
|
||||
but the method still is there and works. To avoid warnings, we declare
|
||||
it ourselves here. */
|
||||
@interface NSApplication(SDL_Missing_Methods)
|
||||
- (void)setAppleMenu:(NSMenu *)menu;
|
||||
@end
|
||||
|
||||
/* Use this flag to determine whether we use SDLMain.nib or not */
|
||||
#define SDL_USE_NIB_FILE 0
|
||||
|
||||
/* Use this flag to determine whether we use CPS (docking) or not */
|
||||
#define SDL_USE_CPS 1
|
||||
#ifdef SDL_USE_CPS
|
||||
/* Portions of CPS.h */
|
||||
typedef struct CPSProcessSerNum
|
||||
{
|
||||
UInt32 lo;
|
||||
UInt32 hi;
|
||||
} CPSProcessSerNum;
|
||||
|
||||
extern "C" {
|
||||
OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
|
||||
OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
|
||||
OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
|
||||
}
|
||||
#endif /* SDL_USE_CPS */
|
||||
|
||||
static int gArgc;
|
||||
static char **gArgv;
|
||||
static BOOL gFinderLaunch;
|
||||
static BOOL gCalledAppMainline = FALSE;
|
||||
|
||||
static NSString *getApplicationName(void)
|
||||
{
|
||||
const NSDictionary *dict;
|
||||
NSString *appName = 0;
|
||||
|
||||
/* Determine the application name */
|
||||
dict = (const NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
|
||||
if (dict)
|
||||
appName = [dict objectForKey: @"CFBundleName"];
|
||||
|
||||
if (![appName length])
|
||||
appName = [[NSProcessInfo processInfo] processName];
|
||||
|
||||
return appName;
|
||||
}
|
||||
|
||||
#if SDL_USE_NIB_FILE
|
||||
/* A helper category for NSString */
|
||||
@interface NSString (ReplaceSubString)
|
||||
- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString;
|
||||
@end
|
||||
#endif
|
||||
|
||||
@interface NSApplication (SDLApplication)
|
||||
@end
|
||||
|
||||
@implementation NSApplication (SDLApplication)
|
||||
/* Invoked from the Quit menu item */
|
||||
- (void)terminate:(id)sender
|
||||
{
|
||||
/* Post a SDL_QUIT event */
|
||||
SDL_Event event;
|
||||
event.type = SDL_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
///// Start of menu items subroutines emendelson
|
||||
|
||||
// link to ReadMeFirst.pdf // emendelson
|
||||
- (void)openReadMeFirst:(id)sender
|
||||
{
|
||||
NSString* helpPath = [[NSBundle mainBundle] pathForResource:@"Read Me First" ofType:@"pdf" inDirectory:@"SheepShaver Help"];
|
||||
[[NSWorkspace sharedWorkspace] openFile:helpPath];
|
||||
}
|
||||
|
||||
// link to setup guide at emaculation.com; webloc file containing URL required as above // emendelson
|
||||
- (void)openSetupGuide:(id)sender
|
||||
{
|
||||
NSString* helpPath = [[NSBundle mainBundle] pathForResource:@"setup" ofType:@"webloc" inDirectory:@"SheepShaver Help"];
|
||||
[[NSWorkspace sharedWorkspace] openFile:helpPath];
|
||||
}
|
||||
|
||||
///// end of menu items subroutines emendelson
|
||||
|
||||
@end
|
||||
|
||||
/* The main class of the application, the application's delegate */
|
||||
@implementation SDLMain
|
||||
|
||||
/* Set the working directory to the .app's parent directory */
|
||||
- (void) setupWorkingDirectory:(BOOL)shouldChdir
|
||||
{
|
||||
if (shouldChdir)
|
||||
{
|
||||
char parentdir[MAXPATHLEN];
|
||||
CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
|
||||
CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
|
||||
if (CFURLGetFileSystemRepresentation(url2, 1, (UInt8 *)parentdir, MAXPATHLEN)) {
|
||||
chdir(parentdir); /* chdir to the binary app's parent */
|
||||
}
|
||||
CFRelease(url);
|
||||
CFRelease(url2);
|
||||
}
|
||||
}
|
||||
|
||||
#if SDL_USE_NIB_FILE
|
||||
|
||||
/* Fix menu to contain the real app name instead of "SDL App" */
|
||||
- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
|
||||
{
|
||||
NSRange aRange;
|
||||
NSEnumerator *enumerator;
|
||||
NSMenuItem *menuItem;
|
||||
|
||||
aRange = [[aMenu title] rangeOfString:@"SDL App"];
|
||||
if (aRange.length != 0)
|
||||
[aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
|
||||
|
||||
enumerator = [[aMenu itemArray] objectEnumerator];
|
||||
while ((menuItem = [enumerator nextObject]))
|
||||
{
|
||||
aRange = [[menuItem title] rangeOfString:@"SDL App"];
|
||||
if (aRange.length != 0)
|
||||
[menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]];
|
||||
if ([menuItem hasSubmenu])
|
||||
[self fixMenu:[menuItem submenu] withAppName:appName];
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
- (void)toggleMenu:(id)sender
|
||||
{
|
||||
gui_open_settings();
|
||||
}
|
||||
|
||||
static void setApplicationMenu(void)
|
||||
{
|
||||
/* warning: this code is very odd */
|
||||
NSMenu *appleMenu;
|
||||
NSMenuItem *menuItem;
|
||||
NSString *title;
|
||||
NSString *appName;
|
||||
|
||||
appName = getApplicationName();
|
||||
appleMenu = [[NSMenu alloc] initWithTitle:@""];
|
||||
|
||||
/* Add menu items */
|
||||
title = [@"About " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
[appleMenu addItemWithTitle:@"Toggle Menu" action:@selector(toggleMenu:) keyEquivalent:@"m"];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
title = [@"Hide " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
|
||||
|
||||
menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
|
||||
[menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
|
||||
|
||||
[appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
title = [@"Quit " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
|
||||
|
||||
|
||||
/* Put menu into the menubar */
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
|
||||
[menuItem setSubmenu:appleMenu];
|
||||
[[NSApp mainMenu] addItem:menuItem];
|
||||
|
||||
/* Tell the application object that this is now the application menu */
|
||||
[NSApp setAppleMenu:appleMenu];
|
||||
|
||||
/* Finally give up our references to the objects */
|
||||
[appleMenu release];
|
||||
[menuItem release];
|
||||
}
|
||||
|
||||
/* Create a window menu */
|
||||
static void setupWindowMenu(void)
|
||||
{
|
||||
NSMenu *windowMenu;
|
||||
NSMenuItem *windowMenuItem;
|
||||
NSMenuItem *menuItem;
|
||||
|
||||
windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||
|
||||
/* "Minimize" item */
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
|
||||
[windowMenu addItem:menuItem];
|
||||
[menuItem release];
|
||||
|
||||
/* Put menu into the menubar */
|
||||
windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
|
||||
[windowMenuItem setSubmenu:windowMenu];
|
||||
[[NSApp mainMenu] addItem:windowMenuItem];
|
||||
|
||||
/* Tell the application object that this is now the window menu */
|
||||
[NSApp setWindowsMenu:windowMenu];
|
||||
|
||||
/* Finally give up our references to the objects */
|
||||
[windowMenu release];
|
||||
[windowMenuItem release];
|
||||
}
|
||||
|
||||
/* Create a help menu - sample entries */
|
||||
static void setupHelpMenu(void)
|
||||
{
|
||||
NSMenu *helpMenu;
|
||||
NSString *title;
|
||||
NSMenuItem *helpMenuItem;
|
||||
NSMenuItem *menuItem;
|
||||
|
||||
helpMenu = [[NSMenu alloc] initWithTitle:@"Help"];
|
||||
|
||||
/* Standard Apple Help item */
|
||||
// NSString *appName = getApplicationName();
|
||||
// title = [appName stringByAppendingString:@" Help"];
|
||||
title = @"SheepShaver Help";
|
||||
|
||||
/* next line requires correctly formatted help book in English.lprog/SheepShaver Help */
|
||||
/* for some reason I can't make "?" work correctly here, so used "/" instead, but could be "" */
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:title action:@selector(showHelp:) keyEquivalent:@"/"];
|
||||
|
||||
/* [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; */
|
||||
|
||||
///// start emendelson
|
||||
|
||||
/* next two lines open local file as described above
|
||||
[helpMenu addItemWithTitle:@"Read Me First (PDF)" action:@selector(openReadMeFirst:) keyEquivalent:@""];
|
||||
[helpMenu addItemWithTitle:@"Setup Guide" action:@selector(openSetupGuide:) keyEquivalent:@"s"];
|
||||
//[helpMenu addItemWithTitle:@"Usage Guide" action:@selector(openInfoPage:) keyEquivalent:@"u"];
|
||||
|
||||
///// end emendelson
|
||||
|
||||
|
||||
[helpMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
[helpMenu addItem:menuItem];
|
||||
[menuItem release];
|
||||
|
||||
/* Put menu into the menubar */
|
||||
helpMenuItem = [[NSMenuItem alloc] initWithTitle:@"Help" action:nil keyEquivalent:@""];
|
||||
[helpMenuItem setSubmenu:helpMenu];
|
||||
[[NSApp mainMenu] addItem:helpMenuItem];
|
||||
|
||||
/* Finally give up our references to the objects */
|
||||
[helpMenu release];
|
||||
[helpMenuItem release];
|
||||
}
|
||||
/* end help menu */
|
||||
|
||||
|
||||
/* Replacement for NSApplicationMain */
|
||||
static void CustomApplicationMain (int argc, char **argv)
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
SDLMain *sdlMain;
|
||||
|
||||
/* Ensure the application object is initialised */
|
||||
[NSApplication sharedApplication];
|
||||
|
||||
#ifdef SDL_USE_CPS
|
||||
{
|
||||
CPSProcessSerNum PSN;
|
||||
/* Tell the dock about us */
|
||||
if (!CPSGetCurrentProcess(&PSN))
|
||||
if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
|
||||
if (!CPSSetFrontProcess(&PSN))
|
||||
[NSApplication sharedApplication];
|
||||
}
|
||||
#endif /* SDL_USE_CPS */
|
||||
|
||||
/* Set up the menubar */
|
||||
[NSApp setMainMenu:[[[NSMenu alloc] init] autorelease]];
|
||||
setApplicationMenu();
|
||||
setupWindowMenu();
|
||||
setupHelpMenu(); /* needed for help menu */
|
||||
|
||||
/* Create SDLMain and make it the app delegate */
|
||||
sdlMain = [[SDLMain alloc] init];
|
||||
[NSApp setDelegate:sdlMain];
|
||||
|
||||
/* Start the main event loop */
|
||||
[NSApp run];
|
||||
|
||||
[sdlMain release];
|
||||
[pool release];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Catch document open requests...this lets us notice files when the app
|
||||
* was launched by double-clicking a document, or when a document was
|
||||
* dragged/dropped on the app's icon. You need to have a
|
||||
* CFBundleDocumentsType section in your Info.plist to get this message,
|
||||
* apparently.
|
||||
*
|
||||
* Files are added to gArgv, so to the app, they'll look like command line
|
||||
* arguments. Previously, apps launched from the finder had nothing but
|
||||
* an argv[0].
|
||||
*
|
||||
* This message may be received multiple times to open several docs on launch.
|
||||
*
|
||||
* This message is ignored once the app's mainline has been called.
|
||||
*/
|
||||
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
|
||||
{
|
||||
const char *temparg;
|
||||
size_t arglen;
|
||||
char *arg;
|
||||
char **newargv;
|
||||
|
||||
if (!gFinderLaunch) /* MacOS is passing command line args. */
|
||||
return FALSE;
|
||||
|
||||
if (gCalledAppMainline) /* app has started, ignore this document. */
|
||||
return FALSE;
|
||||
|
||||
temparg = [filename UTF8String];
|
||||
arglen = SDL_strlen(temparg) + 1;
|
||||
arg = (char *) SDL_malloc(arglen);
|
||||
if (arg == NULL)
|
||||
return FALSE;
|
||||
|
||||
newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2));
|
||||
if (newargv == NULL)
|
||||
{
|
||||
SDL_free(arg);
|
||||
return FALSE;
|
||||
}
|
||||
gArgv = newargv;
|
||||
|
||||
SDL_strlcpy(arg, temparg, arglen);
|
||||
gArgv[gArgc++] = arg;
|
||||
gArgv[gArgc] = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* Called when the internal event loop has just started running */
|
||||
- (void) applicationDidFinishLaunching: (NSNotification *) note
|
||||
{
|
||||
int status;
|
||||
|
||||
/* Set the working directory to the .app's parent directory */
|
||||
[self setupWorkingDirectory:gFinderLaunch];
|
||||
|
||||
#if SDL_USE_NIB_FILE
|
||||
/* Set the main menu to contain the real app name instead of "SDL App" */
|
||||
[self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()];
|
||||
#endif
|
||||
|
||||
/* Hand off to main application code */
|
||||
gCalledAppMainline = TRUE;
|
||||
status = SDL_main (gArgc, gArgv);
|
||||
|
||||
/* We're done, thank you for playing */
|
||||
exit(status);
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@implementation NSString (ReplaceSubString)
|
||||
|
||||
- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
|
||||
{
|
||||
unsigned int bufferSize;
|
||||
unsigned int selfLen = [self length];
|
||||
unsigned int aStringLen = [aString length];
|
||||
unichar *buffer;
|
||||
NSRange localRange;
|
||||
NSString *result;
|
||||
|
||||
bufferSize = selfLen + aStringLen - aRange.length;
|
||||
buffer = (unichar *)NSAllocateMemoryPages(bufferSize*sizeof(unichar));
|
||||
|
||||
/* Get first part into buffer */
|
||||
localRange.location = 0;
|
||||
localRange.length = aRange.location;
|
||||
[self getCharacters:buffer range:localRange];
|
||||
|
||||
/* Get middle part into buffer */
|
||||
localRange.location = 0;
|
||||
localRange.length = aStringLen;
|
||||
[aString getCharacters:(buffer+aRange.location) range:localRange];
|
||||
|
||||
/* Get last part into buffer */
|
||||
localRange.location = aRange.location + aRange.length;
|
||||
localRange.length = selfLen - localRange.location;
|
||||
[self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
|
||||
|
||||
/* Build output string */
|
||||
result = [NSString stringWithCharacters:buffer length:bufferSize];
|
||||
|
||||
NSDeallocateMemoryPages(buffer, bufferSize);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
#ifdef main
|
||||
# undef main
|
||||
#endif
|
||||
|
||||
|
||||
/* Main entry point to executable - should *not* be SDL_main! */
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
/* Copy the arguments into a global variable */
|
||||
/* This is passed if we are launched by double-clicking */
|
||||
if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
|
||||
gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
|
||||
gArgv[0] = argv[0];
|
||||
gArgv[1] = NULL;
|
||||
gArgc = 1;
|
||||
gFinderLaunch = YES;
|
||||
} else {
|
||||
int i;
|
||||
gArgc = argc;
|
||||
gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
|
||||
for (i = 0; i <= argc; i++)
|
||||
gArgv[i] = argv[i];
|
||||
gFinderLaunch = NO;
|
||||
}
|
||||
|
||||
#if SDL_USE_NIB_FILE
|
||||
NSApplicationMain (argc, argv);
|
||||
#else
|
||||
CustomApplicationMain (argc, argv);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
//
|
||||
// osx-main-Bridging-Header.h
|
||||
// emulator-osx
|
||||
//
|
||||
// Created by admin on 8/5/15.
|
||||
// Copyright (c) 2015 reicast. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef emulator_osx_osx_main_Bridging_Header_h
|
||||
#define emulator_osx_osx_main_Bridging_Header_h
|
||||
#include <MacTypes.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void emu_flycast_term();
|
||||
void emu_gui_open_settings();
|
||||
bool emu_renderer_enabled();
|
||||
bool emu_fast_forward();
|
||||
bool emu_vsync_enabled();
|
||||
bool emu_single_frame(int w, int h);
|
||||
void emu_gles_init(int width, int height);
|
||||
int emu_flycast_init();
|
||||
void emu_key_input(UInt16 keyCode, bool pressed, UInt32 modifierFlags);
|
||||
void emu_character_input(const char *characters);
|
||||
void emu_mouse_buttons(int button, bool pressed);
|
||||
void emu_set_mouse_position(int x, int y, int width, int height);
|
||||
void emu_mouse_wheel(float v);
|
||||
|
||||
bool emu_frame_pending();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -7,33 +7,22 @@
|
|||
//
|
||||
#import <Carbon/Carbon.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
#include <OpenGL/gl3.h>
|
||||
#include <sys/stat.h>
|
||||
#include <mach/task.h>
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/mach_port.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "hw/maple/maple_cfg.h"
|
||||
#include "hw/maple/maple_devs.h"
|
||||
#include "log/LogManager.h"
|
||||
#include "rend/gui.h"
|
||||
#include "osx_keyboard.h"
|
||||
#include "osx_gamepad.h"
|
||||
#include "emulator-osx-Bridging-Header.h"
|
||||
#if defined(USE_SDL)
|
||||
#include "sdl/sdl.h"
|
||||
#endif
|
||||
#include "stdclass.h"
|
||||
#include "wsi/context.h"
|
||||
#include "oslib/oslib.h"
|
||||
#include "emulator.h"
|
||||
#include "hw/pvr/Renderer_if.h"
|
||||
#include "rend/mainui.h"
|
||||
|
||||
static std::shared_ptr<OSXKeyboard> keyboard(0);
|
||||
static std::shared_ptr<OSXMouse> mouse;
|
||||
static UInt32 keyboardModifiers;
|
||||
|
||||
int darw_printf(const char* text, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
@ -74,6 +63,7 @@ void os_CreateWindow() {
|
|||
printf("task_set_exception_ports: %s\n", mach_error_string(ret));
|
||||
}
|
||||
#endif
|
||||
sdl_window_create();
|
||||
}
|
||||
|
||||
void os_SetupInput()
|
||||
|
@ -81,81 +71,27 @@ void os_SetupInput()
|
|||
#if defined(USE_SDL)
|
||||
input_sdl_init();
|
||||
#endif
|
||||
|
||||
keyboard = std::make_shared<OSXKeyboard>(0);
|
||||
GamepadDevice::Register(keyboard);
|
||||
mouse = std::make_shared<OSXMouse>();
|
||||
GamepadDevice::Register(mouse);
|
||||
}
|
||||
|
||||
void common_linux_setup();
|
||||
static int emu_flycast_init();
|
||||
|
||||
void emu_flycast_term()
|
||||
static void emu_flycast_term()
|
||||
{
|
||||
flycast_term();
|
||||
LogManager::Shutdown();
|
||||
}
|
||||
|
||||
void emu_gui_open_settings()
|
||||
{
|
||||
gui_open_settings();
|
||||
}
|
||||
|
||||
extern bool rend_framePending();
|
||||
|
||||
bool emu_frame_pending()
|
||||
{
|
||||
return rend_framePending() || !emu.running() || gui_is_open() || !config::ThreadedRendering;
|
||||
}
|
||||
|
||||
bool emu_renderer_enabled()
|
||||
{
|
||||
return mainui_loop_enabled();
|
||||
}
|
||||
|
||||
bool emu_fast_forward()
|
||||
{
|
||||
return settings.input.fastForwardMode;
|
||||
}
|
||||
|
||||
bool emu_vsync_enabled()
|
||||
{
|
||||
return config::VSync;
|
||||
}
|
||||
|
||||
bool emu_single_frame(int w, int h)
|
||||
{
|
||||
settings.display.width = w;
|
||||
settings.display.height = h;
|
||||
|
||||
//For DelayFrameSwapping: use while loop to call multple mainui_rend_frame() until rend_swap_frame(u32 fb_r_sof1)
|
||||
int counter = 0;
|
||||
while (mainui_enabled && counter < 5)
|
||||
{
|
||||
counter++;
|
||||
if (mainui_rend_frame())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void emu_gles_init(int width, int height)
|
||||
extern "C" int SDL_main(int argc, char *argv[])
|
||||
{
|
||||
char *home = getenv("HOME");
|
||||
if (home != NULL)
|
||||
{
|
||||
std::string config_dir = std::string(home) + "/.reicast/";
|
||||
if (!file_exists(config_dir))
|
||||
config_dir = std::string(home) + "/.flycast/";
|
||||
config_dir = std::string(home) + "/.flycast/";
|
||||
if (!file_exists(config_dir))
|
||||
config_dir = std::string(home) + "/Library/Application Support/Flycast/";
|
||||
int instanceNumber = (int)[[NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.flyinghead.Flycast"] count];
|
||||
if (instanceNumber > 1){
|
||||
config_dir += std::to_string(instanceNumber) + "/";
|
||||
[[NSApp dockTile] setBadgeLabel:@(instanceNumber).stringValue];
|
||||
}
|
||||
mkdir(config_dir.c_str(), 0755); // create the directory if missing
|
||||
set_user_config_dir(config_dir);
|
||||
add_system_data_dir(config_dir);
|
||||
|
@ -177,63 +113,67 @@ void emu_gles_init(int width, int height)
|
|||
CFRelease(resourcesURL);
|
||||
CFRelease(mainBundle);
|
||||
|
||||
// Calculate screen DPI
|
||||
NSScreen *screen = [NSScreen mainScreen];
|
||||
NSDictionary *description = [screen deviceDescription];
|
||||
// Calculate screen DPI
|
||||
/*
|
||||
NSScreen *screen = [NSScreen mainScreen];
|
||||
NSDictionary *description = [screen deviceDescription];
|
||||
CGDirectDisplayID displayID = [[description objectForKey:@"NSScreenNumber"] unsignedIntValue];
|
||||
CGSize displayPhysicalSize = CGDisplayScreenSize(displayID);
|
||||
|
||||
//Neither CGDisplayScreenSize(description's NSScreenNumber) nor [NSScreen backingScaleFactor] can calculate the correct dpi in macOS. E.g. backingScaleFactor is always 2 in all display modes for rMBP 16"
|
||||
NSSize displayNativeSize;
|
||||
CFStringRef dmKeys[1] = { kCGDisplayShowDuplicateLowResolutionModes };
|
||||
CFBooleanRef dmValues[1] = { kCFBooleanTrue };
|
||||
CFDictionaryRef dmOptions = CFDictionaryCreate(kCFAllocatorDefault, (const void**) dmKeys, (const void**) dmValues, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
|
||||
CFArrayRef allDisplayModes = CGDisplayCopyAllDisplayModes(displayID, dmOptions);
|
||||
CFIndex n = CFArrayGetCount(allDisplayModes);
|
||||
for (CFIndex i = 0; i < n; ++i)
|
||||
{
|
||||
CGDisplayModeRef m = (CGDisplayModeRef)CFArrayGetValueAtIndex(allDisplayModes, i);
|
||||
CGFloat width = CGDisplayModeGetPixelWidth(m);
|
||||
CGFloat height = CGDisplayModeGetPixelHeight(m);
|
||||
CGFloat modeWidth = CGDisplayModeGetWidth(m);
|
||||
|
||||
//Only check 1x mode
|
||||
if (width == modeWidth)
|
||||
{
|
||||
if (CGDisplayModeGetIOFlags(m) & kDisplayModeNativeFlag)
|
||||
{
|
||||
displayNativeSize.width = width;
|
||||
displayNativeSize.height = height;
|
||||
break;
|
||||
}
|
||||
|
||||
//Get the largest size even if kDisplayModeNativeFlag is not present e.g. iMac 27-Inch with 5K Retina
|
||||
if (width > displayNativeSize.width)
|
||||
{
|
||||
displayNativeSize.width = width;
|
||||
displayNativeSize.height = height;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
CFRelease(allDisplayModes);
|
||||
CFRelease(dmOptions);
|
||||
|
||||
screen_dpi = (int)(displayNativeSize.width / displayPhysicalSize.width * 25.4f);
|
||||
NSSize displayResolution;
|
||||
displayResolution.width = CGDisplayPixelsWide(displayID);
|
||||
displayResolution.height = CGDisplayPixelsHigh(displayID);
|
||||
scaling = displayNativeSize.width / displayResolution.width;
|
||||
|
||||
settings.display.width = width;
|
||||
settings.display.height = height;
|
||||
|
||||
InitRenderApi();
|
||||
mainui_init();
|
||||
mainui_enabled = true;
|
||||
//Neither CGDisplayScreenSize(description's NSScreenNumber) nor [NSScreen backingScaleFactor] can calculate the correct dpi in macOS. E.g. backingScaleFactor is always 2 in all display modes for rMBP 16"
|
||||
NSSize displayNativeSize;
|
||||
CFStringRef dmKeys[1] = { kCGDisplayShowDuplicateLowResolutionModes };
|
||||
CFBooleanRef dmValues[1] = { kCFBooleanTrue };
|
||||
CFDictionaryRef dmOptions = CFDictionaryCreate(kCFAllocatorDefault, (const void**) dmKeys, (const void**) dmValues, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
|
||||
CFArrayRef allDisplayModes = CGDisplayCopyAllDisplayModes(displayID, dmOptions);
|
||||
CFIndex n = CFArrayGetCount(allDisplayModes);
|
||||
for (CFIndex i = 0; i < n; ++i)
|
||||
{
|
||||
CGDisplayModeRef m = (CGDisplayModeRef)CFArrayGetValueAtIndex(allDisplayModes, i);
|
||||
CGFloat width = CGDisplayModeGetPixelWidth(m);
|
||||
CGFloat height = CGDisplayModeGetPixelHeight(m);
|
||||
CGFloat modeWidth = CGDisplayModeGetWidth(m);
|
||||
|
||||
//Only check 1x mode
|
||||
if (width == modeWidth)
|
||||
{
|
||||
if (CGDisplayModeGetIOFlags(m) & kDisplayModeNativeFlag)
|
||||
{
|
||||
displayNativeSize.width = width;
|
||||
displayNativeSize.height = height;
|
||||
break;
|
||||
}
|
||||
//Get the largest size even if kDisplayModeNativeFlag is not present e.g. iMac 27-Inch with 5K Retina
|
||||
if (width > displayNativeSize.width)
|
||||
{
|
||||
displayNativeSize.width = width;
|
||||
displayNativeSize.height = height;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
CFRelease(allDisplayModes);
|
||||
CFRelease(dmOptions);
|
||||
|
||||
screen_dpi = (int)(displayNativeSize.width / displayPhysicalSize.width * 25.4f);
|
||||
NSSize displayResolution;
|
||||
displayResolution.width = CGDisplayPixelsWide(displayID);
|
||||
displayResolution.height = CGDisplayPixelsHigh(displayID);
|
||||
scaling = displayNativeSize.width / displayResolution.width;
|
||||
*/
|
||||
|
||||
emu_flycast_init();
|
||||
|
||||
mainui_loop();
|
||||
|
||||
emu_flycast_term();
|
||||
os_UninstallFaultHandler();
|
||||
sdl_window_destroy();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int emu_flycast_init()
|
||||
static int emu_flycast_init()
|
||||
{
|
||||
LogManager::Init();
|
||||
common_linux_setup();
|
||||
|
@ -259,57 +199,6 @@ int emu_flycast_init()
|
|||
return rc;
|
||||
}
|
||||
|
||||
void emu_key_input(UInt16 keyCode, bool pressed, UInt modifierFlags) {
|
||||
if (keyCode != 0xFF)
|
||||
keyboard->keyboard_input(keyCode, pressed, 0);
|
||||
else
|
||||
{
|
||||
// Modifier keys
|
||||
UInt32 changes = keyboardModifiers ^ modifierFlags;
|
||||
if (changes & NSEventModifierFlagShift)
|
||||
keyboard->keyboard_input(kVK_Shift, modifierFlags & NSEventModifierFlagShift, 0);
|
||||
if (changes & NSEventModifierFlagControl)
|
||||
keyboard->keyboard_input(kVK_Control, modifierFlags & NSEventModifierFlagControl, 0);
|
||||
if (changes & NSEventModifierFlagOption)
|
||||
keyboard->keyboard_input(kVK_Option, modifierFlags & NSEventModifierFlagOption, 0);
|
||||
keyboardModifiers = modifierFlags;
|
||||
}
|
||||
}
|
||||
void emu_character_input(const char *characters) {
|
||||
if (characters != NULL)
|
||||
gui_keyboard_inputUTF8(characters);
|
||||
}
|
||||
|
||||
void emu_mouse_buttons(int button, bool pressed)
|
||||
{
|
||||
Mouse::Button dcButton;
|
||||
switch (button) {
|
||||
case 1:
|
||||
dcButton = Mouse::LEFT_BUTTON;
|
||||
break;
|
||||
case 2:
|
||||
dcButton = Mouse::RIGHT_BUTTON;
|
||||
break;
|
||||
case 3:
|
||||
dcButton = Mouse::MIDDLE_BUTTON;
|
||||
break;
|
||||
default:
|
||||
dcButton = Mouse::BUTTON_4;
|
||||
break;
|
||||
}
|
||||
mouse->setButton(dcButton, pressed);
|
||||
}
|
||||
|
||||
void emu_mouse_wheel(float v)
|
||||
{
|
||||
mouse->setWheel((int)v);
|
||||
}
|
||||
|
||||
void emu_set_mouse_position(int x, int y, int width, int height)
|
||||
{
|
||||
mouse->setAbsPos(x, y, width, height);
|
||||
}
|
||||
|
||||
std::string os_Locale(){
|
||||
return [[[NSLocale preferredLanguages] objectAtIndex:0] UTF8String];
|
||||
}
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
//
|
||||
// osx_gamepad.h
|
||||
// reicast-osx
|
||||
//
|
||||
// Created by flyinghead on 26/02/2019.
|
||||
// Copyright © 2019 reicast. All rights reserved.
|
||||
//
|
||||
#include "input/mouse.h"
|
||||
|
||||
class OSXMouse : public SystemMouse
|
||||
{
|
||||
public:
|
||||
OSXMouse() : SystemMouse("OSX")
|
||||
{
|
||||
_unique_id = "osx_mouse";
|
||||
loadMapping();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1,230 +0,0 @@
|
|||
//
|
||||
// osx_keyboard.h
|
||||
// reicast-osx
|
||||
//
|
||||
// Created by flyinghead on 26/02/2019.
|
||||
// Copyright © 2019 reicast. All rights reserved.
|
||||
//
|
||||
#pragma once
|
||||
#include "input/keyboard_device.h"
|
||||
#include "oslib/oslib.h"
|
||||
|
||||
// Rumbling Taptic Engine by Private MultitouchSupport.framework
|
||||
extern "C" {
|
||||
typedef void *MTDeviceRef;
|
||||
bool MTDeviceIsAvailable(void);
|
||||
MTDeviceRef MTDeviceCreateDefault(void);
|
||||
OSStatus MTDeviceGetDeviceID(MTDeviceRef, uint64_t*) __attribute__ ((weak_import));
|
||||
CFTypeRef MTActuatorCreateFromDeviceID(UInt64 deviceID);
|
||||
IOReturn MTActuatorOpen(CFTypeRef actuatorRef);
|
||||
IOReturn MTActuatorClose(CFTypeRef actuatorRef);
|
||||
IOReturn MTActuatorActuate(CFTypeRef actuatorRef, SInt32 actuationID, UInt32 unknown1, Float32 unknown2, Float32 unknown3);
|
||||
bool MTActuatorIsOpen(CFTypeRef actuatorRef);
|
||||
}
|
||||
|
||||
class OSXKeyboard : public KeyboardDeviceTemplate<UInt16>
|
||||
{
|
||||
public:
|
||||
OSXKeyboard(int maple_port) : KeyboardDeviceTemplate(maple_port, "OSX")
|
||||
{
|
||||
_name = "Keyboard";
|
||||
_unique_id = "osx_keyboard";
|
||||
loadMapping();
|
||||
|
||||
//04-1D Letter keys A-Z (in alphabetic order)
|
||||
kb_map[kVK_ANSI_A] = 0x04;
|
||||
kb_map[kVK_ANSI_B] = 0x05;
|
||||
kb_map[kVK_ANSI_C] = 0x06;
|
||||
kb_map[kVK_ANSI_D] = 0x07;
|
||||
kb_map[kVK_ANSI_E] = 0x08;
|
||||
kb_map[kVK_ANSI_F] = 0x09;
|
||||
kb_map[kVK_ANSI_G] = 0x0A;
|
||||
kb_map[kVK_ANSI_H] = 0x0B;
|
||||
kb_map[kVK_ANSI_I] = 0x0C;
|
||||
kb_map[kVK_ANSI_J] = 0x0D;
|
||||
kb_map[kVK_ANSI_K] = 0x0E;
|
||||
kb_map[kVK_ANSI_L] = 0x0F;
|
||||
kb_map[kVK_ANSI_M] = 0x10;
|
||||
kb_map[kVK_ANSI_N] = 0x11;
|
||||
kb_map[kVK_ANSI_O] = 0x12;
|
||||
kb_map[kVK_ANSI_P] = 0x13;
|
||||
kb_map[kVK_ANSI_Q] = 0x14;
|
||||
kb_map[kVK_ANSI_R] = 0x15;
|
||||
kb_map[kVK_ANSI_S] = 0x16;
|
||||
kb_map[kVK_ANSI_T] = 0x17;
|
||||
kb_map[kVK_ANSI_U] = 0x18;
|
||||
kb_map[kVK_ANSI_V] = 0x19;
|
||||
kb_map[kVK_ANSI_W] = 0x1A;
|
||||
kb_map[kVK_ANSI_X] = 0x1B;
|
||||
kb_map[kVK_ANSI_Y] = 0x1C;
|
||||
kb_map[kVK_ANSI_Z] = 0x1D;
|
||||
|
||||
//1E-27 Number keys 1-0
|
||||
kb_map[kVK_ANSI_1] = 0x1E;
|
||||
kb_map[kVK_ANSI_2] = 0x1F;
|
||||
kb_map[kVK_ANSI_3] = 0x20;
|
||||
kb_map[kVK_ANSI_4] = 0x21;
|
||||
kb_map[kVK_ANSI_5] = 0x22;
|
||||
kb_map[kVK_ANSI_6] = 0x23;
|
||||
kb_map[kVK_ANSI_7] = 0x24;
|
||||
kb_map[kVK_ANSI_8] = 0x25;
|
||||
kb_map[kVK_ANSI_9] = 0x26;
|
||||
kb_map[kVK_ANSI_0] = 0x27;
|
||||
|
||||
kb_map[kVK_Return] = 0x28;
|
||||
kb_map[kVK_Escape] = 0x29;
|
||||
kb_map[kVK_Delete] = 0x2A;
|
||||
kb_map[kVK_Tab] = 0x2B;
|
||||
kb_map[kVK_Space] = 0x2C;
|
||||
|
||||
kb_map[kVK_ANSI_Minus] = 0x2D; // -
|
||||
kb_map[kVK_ANSI_Equal] = 0x2E; // =
|
||||
kb_map[kVK_ANSI_LeftBracket] = 0x2F; // [
|
||||
kb_map[kVK_ANSI_RightBracket] = 0x30; // ]
|
||||
|
||||
kb_map[kVK_ANSI_Backslash] = 0x31; // \ (US) unsure of keycode
|
||||
|
||||
//32-34 "]", ";" and ":" (the 3 keys right of L)
|
||||
//kb_map[?] = 0x32; // ~ (non-US) *,µ in FR layout
|
||||
kb_map[kVK_ANSI_Semicolon] = 0x33; // ;
|
||||
kb_map[kVK_ANSI_Quote] = 0x34; // '
|
||||
|
||||
//35 hankaku/zenkaku / kanji (top left)
|
||||
kb_map[kVK_ANSI_Grave] = 0x35; // `~ (US)
|
||||
|
||||
//36-38 ",", "." and "/" (the 3 keys right of M)
|
||||
kb_map[kVK_ANSI_Comma] = 0x36;
|
||||
kb_map[kVK_ANSI_Period] = 0x37;
|
||||
kb_map[kVK_ANSI_Slash] = 0x38;
|
||||
|
||||
// CAPSLOCK
|
||||
kb_map[kVK_CapsLock] = 0x39;
|
||||
|
||||
//3A-45 Function keys F1-F12
|
||||
kb_map[kVK_F1] = 0x3A;
|
||||
kb_map[kVK_F2] = 0x3B;
|
||||
kb_map[kVK_F3] = 0x3C;
|
||||
kb_map[kVK_F4] = 0x3D;
|
||||
kb_map[kVK_F5] = 0x3E;
|
||||
kb_map[kVK_F6] = 0x3F;
|
||||
kb_map[kVK_F7] = 0x40;
|
||||
kb_map[kVK_F8] = 0x41;
|
||||
kb_map[kVK_F9] = 0x42;
|
||||
kb_map[kVK_F10] = 0x43;
|
||||
kb_map[kVK_F11] = 0x44;
|
||||
kb_map[kVK_F12] = 0x45;
|
||||
|
||||
//46-4E Control keys above cursor keys
|
||||
kb_map[kVK_F13] = 0x46; // Print Screen
|
||||
kb_map[kVK_F14] = 0x47; // Scroll Lock
|
||||
kb_map[kVK_F15] = 0x48; // Pause
|
||||
kb_map[kVK_Help] = 0x49; // Insert
|
||||
kb_map[kVK_Home] = 0x4A;
|
||||
kb_map[kVK_PageUp] = 0x4B;
|
||||
kb_map[kVK_ForwardDelete] = 0x4C;
|
||||
kb_map[kVK_End] = 0x4D;
|
||||
kb_map[kVK_PageDown] = 0x4E;
|
||||
|
||||
//4F-52 Cursor keys
|
||||
kb_map[kVK_RightArrow] = 0x4F;
|
||||
kb_map[kVK_LeftArrow] = 0x50;
|
||||
kb_map[kVK_DownArrow] = 0x51;
|
||||
kb_map[kVK_UpArrow] = 0x52;
|
||||
|
||||
//53 Num Lock (Numeric keypad)
|
||||
kb_map[kVK_ANSI_KeypadClear] = 0x53;
|
||||
//54 "/" (Numeric keypad)
|
||||
kb_map[kVK_ANSI_KeypadDivide] = 0x54;
|
||||
//55 "*" (Numeric keypad)
|
||||
kb_map[kVK_ANSI_KeypadMultiply] = 0x55;
|
||||
//56 "-" (Numeric keypad)
|
||||
kb_map[kVK_ANSI_KeypadMinus] = 0x56;
|
||||
//57 "+" (Numeric keypad)
|
||||
kb_map[kVK_ANSI_KeypadPlus] = 0x57;
|
||||
//58 Enter (Numeric keypad)
|
||||
kb_map[kVK_ANSI_KeypadEnter] = 0x58;
|
||||
//59-62 Number keys 1-0 (Numeric keypad)
|
||||
kb_map[kVK_ANSI_Keypad1] = 0x59;
|
||||
kb_map[kVK_ANSI_Keypad2] = 0x5A;
|
||||
kb_map[kVK_ANSI_Keypad3] = 0x5B;
|
||||
kb_map[kVK_ANSI_Keypad4] = 0x5C;
|
||||
kb_map[kVK_ANSI_Keypad5] = 0x5D;
|
||||
kb_map[kVK_ANSI_Keypad6] = 0x5E;
|
||||
kb_map[kVK_ANSI_Keypad7] = 0x5F;
|
||||
kb_map[kVK_ANSI_Keypad8] = 0x60;
|
||||
kb_map[kVK_ANSI_Keypad9] = 0x61;
|
||||
kb_map[kVK_ANSI_Keypad0] = 0x62;
|
||||
//63 "." (Numeric keypad)
|
||||
kb_map[kVK_ANSI_KeypadDecimal] = 0x63;
|
||||
//64 #| (non-US)
|
||||
//kb_map[94] = 0x64;
|
||||
//65 S3 key
|
||||
//66-A4 Not used
|
||||
//A5-DF Reserved
|
||||
kb_map[kVK_Control] = 0xE0;
|
||||
kb_map[kVK_Shift] = 0xE1;
|
||||
kb_map[kVK_Option] = 0xE2; // Left Alt
|
||||
//E3 Left S1
|
||||
kb_map[kVK_RightControl] = 0xE4;
|
||||
kb_map[kVK_RightShift] = 0xE5;
|
||||
kb_map[kVK_RightOption] = 0xE6; // Right Alt
|
||||
//E7 Right S3
|
||||
//E8-FF Reserved
|
||||
|
||||
kb_map[kVK_ISO_Section] = 0x32; // #, Tilde
|
||||
|
||||
// Japanese keyboards
|
||||
kb_map[kVK_JIS_Underscore] = 0x87; // I18n keyboard 1
|
||||
kb_map[kVK_JIS_Yen] = 0x89; // I18n keyboard 3
|
||||
}
|
||||
|
||||
void rumble(float power, float inclination, u32 duration_ms) override
|
||||
{
|
||||
NOTICE_LOG(INPUT, "rumble %.1f inc %f duration %d", power, inclination, duration_ms);
|
||||
|
||||
uint64_t deviceID;
|
||||
if ( MTDeviceIsAvailable() && MTDeviceGetDeviceID(MTDeviceCreateDefault(), &deviceID) == 0 )
|
||||
{
|
||||
if ( power == 0 && vib_timer )
|
||||
{
|
||||
dispatch_source_cancel(vib_timer);
|
||||
return;
|
||||
}
|
||||
|
||||
__block CFTypeRef actuatorRef = MTActuatorCreateFromDeviceID(deviceID);
|
||||
if (!actuatorRef) return;
|
||||
MTActuatorOpen(actuatorRef);
|
||||
|
||||
__block double vib_stop_time = os_GetSeconds() + duration_ms / 1000.0;
|
||||
vib_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0));
|
||||
// Vibration interval: Power 1.0 = 10ms, Power 0.1 = 100ms
|
||||
dispatch_source_set_timer(vib_timer, DISPATCH_TIME_NOW, 10 + (1.0-power)*100 * NSEC_PER_MSEC, 0);
|
||||
|
||||
dispatch_source_set_event_handler(vib_timer, ^{
|
||||
if ( vib_stop_time - os_GetSeconds() < 0 )
|
||||
{
|
||||
dispatch_source_cancel(vib_timer);
|
||||
return;
|
||||
}
|
||||
MTActuatorActuate(actuatorRef, 6, 0, 0.0, 0.0);
|
||||
});
|
||||
|
||||
dispatch_source_set_cancel_handler(vib_timer, ^{
|
||||
MTActuatorClose(actuatorRef);
|
||||
});
|
||||
|
||||
dispatch_resume(vib_timer);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
u8 convert_keycode(UInt16 keycode) override
|
||||
{
|
||||
return kb_map[keycode];
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<UInt16, u8> kb_map;
|
||||
dispatch_source_t vib_timer;
|
||||
};
|
||||
|
Loading…
Reference in New Issue