vulkan: moltenvk support on macOS - WIP

sdl: save/restore window position in config
This commit is contained in:
Flyinghead 2021-11-01 13:05:22 +01:00
parent 46ae8f92cc
commit da3ed74c4e
42 changed files with 19056 additions and 17217 deletions

View File

@ -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'

View File

@ -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)

View File

@ -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)

View File

@ -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);
}

View File

@ -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;

View File

@ -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++);

View File

@ -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));

View File

@ -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;

View File

@ -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();

View File

@ -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();

View File

@ -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)
{

View File

@ -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)

View File

@ -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;
};

View File

@ -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();

View File

@ -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
(

View File

@ -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

View File

@ -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()

View File

@ -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;
};

View File

@ -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);

View File

@ -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

View File

@ -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);
}

View File

@ -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:

View File

@ -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();

View File

@ -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;

View File

@ -77,6 +77,7 @@ protected:
public:
void Term() override
{
GetContext()->WaitIdle();
GetContext()->PresentFrame(nullptr, nullptr, vk::Extent2D());
#ifdef LIBRETRO
overlay->Term();

View File

@ -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__)

View File

@ -49,7 +49,7 @@ private:
#include "libretro.h"
#elif defined(__APPLE__)
#elif defined(TARGET_IPHONE)
#include "osx.h"

View File

@ -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;

View File

@ -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

View File

@ -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");

View File

@ -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>

View File

@ -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>

View File

@ -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])
}
}

View File

@ -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>

View File

@ -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();
}
}

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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

View File

@ -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];
}

View File

@ -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();
}
};

View File

@ -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;
};