Skip to content

Commit

Permalink
Merge pull request google#342 from brenwill/master
Browse files Browse the repository at this point in the history
Add support for iOS and macOS via MoltenVK
  • Loading branch information
SaschaWillems authored Jun 29, 2017
2 parents 40ba8f1 + 34ede25 commit b1da3e2
Show file tree
Hide file tree
Showing 47 changed files with 2,098 additions and 11 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ Note that you need [assimp](https://github.com/assimp/assimp) in order to compil

Building on Android is done using the [Android NDK](http://developer.android.com/tools/sdk/ndk/index.html) and requires a device that supports Vulkan. Please see the [Android readme](./android/README.md) on how to build and deploy the examples.

## <img src="./images/applelogo.png" alt="" height="32px"> [iOS and macOS](xcode/)

Building for *iOS* and *macOS* is done using the [examples](xcode/examples.xcodeproj) *Xcode* project found in the [xcode](xcode) directory. These examples use the [**MoltenVK**](https://moltengl.com/moltenvk) Vulkan driver to provide Vulkan support on *iOS* and *macOS*, and require an *iOS* or *macOS* device that supports *Metal*. Please see the [MoltenVK Examples readme](xcode/README_MoltenVK_Examples.md) for more info on acquiring **MoltenVK** and building and deploying the examples on *iOS* and *macOS*.

## Additional asset pack

**Note:** Binary assets (textures, models) will no longer be added directly to the repository to keep it's size down, so newer examples will require the download of an [additional asset pack](data/README.md).
Expand Down
22 changes: 22 additions & 0 deletions base/VulkanSwapChain.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ class VulkanSwapChain
#ifdef __ANDROID__
ANativeWindow* window
#else
#if (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
void* view
#else
#ifdef _DIRECT2DISPLAY
uint32_t width, uint32_t height
#else
Expand All @@ -117,6 +120,7 @@ class VulkanSwapChain
#endif
#endif
#endif
#endif
#endif
)
{
Expand All @@ -136,6 +140,22 @@ class VulkanSwapChain
surfaceCreateInfo.window = window;
err = vkCreateAndroidSurfaceKHR(instance, &surfaceCreateInfo, NULL, &surface);
#else
#ifdef VK_USE_PLATFORM_IOS_MVK
VkIOSSurfaceCreateInfoMVK surfaceCreateInfo = {};
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
surfaceCreateInfo.pNext = NULL;
surfaceCreateInfo.flags = 0;
surfaceCreateInfo.pView = view;
err = vkCreateIOSSurfaceMVK(instance, &surfaceCreateInfo, nullptr, &surface);
#else
#ifdef VK_USE_PLATFORM_MACOS_MVK
VkMacOSSurfaceCreateInfoMVK surfaceCreateInfo = {};
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
surfaceCreateInfo.pNext = NULL;
surfaceCreateInfo.flags = 0;
surfaceCreateInfo.pView = view;
err = vkCreateMacOSSurfaceMVK(instance, &surfaceCreateInfo, NULL, &surface);
#else
#if defined(_DIRECT2DISPLAY)
createDirect2DisplaySurface(width, height);
#else
Expand All @@ -154,6 +174,8 @@ class VulkanSwapChain
#endif
#endif
#endif
#endif
#endif
#endif

if (err != VK_SUCCESS) {
Expand Down
50 changes: 50 additions & 0 deletions base/keycodes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,56 @@
#define KEY_N 0xE
#define KEY_O 0xF
#define KEY_T 0x10

#elif defined(VK_USE_PLATFORM_IOS_MVK)
// Use numeric keys instead of function keys.
// Use main keyboard plus/minus instead of keypad plus/minus
// Use Delete key instead of Escape key.
#define KEY_ESCAPE 0x33
#define KEY_F1 '1'
#define KEY_F2 '2'
#define KEY_F3 '3'
#define KEY_F4 '4'
#define KEY_W 'w'
#define KEY_A 'a'
#define KEY_S 's'
#define KEY_D 'd'
#define KEY_P 'p'
#define KEY_SPACE ' '
#define KEY_KPADD '+'
#define KEY_KPSUB '-'
#define KEY_B 'b'
#define KEY_F 'f'
#define KEY_L 'l'
#define KEY_N 'n'
#define KEY_O 'o'
#define KEY_T 't'

#elif defined(VK_USE_PLATFORM_MACOS_MVK)
// For compatibility with iOS UX and absent keypad on MacBook:
// - Use numeric keys instead of function keys
// - Use main keyboard plus/minus instead of keypad plus/minus
// - Use Delete key instead of Escape key
#define KEY_ESCAPE 0x33
#define KEY_F1 0x12
#define KEY_F2 0x13
#define KEY_F3 0x14
#define KEY_F4 0x15
#define KEY_W 0x0D
#define KEY_A 0x00
#define KEY_S 0x01
#define KEY_D 0x02
#define KEY_P 0x23
#define KEY_SPACE 0x31
#define KEY_KPADD 0x18
#define KEY_KPSUB 0x1B
#define KEY_B 0x0B
#define KEY_F 0x03
#define KEY_L 0x25
#define KEY_N 0x2D
#define KEY_O 0x1F
#define KEY_T 0x11

#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
#include <linux/input.h>

Expand Down
50 changes: 50 additions & 0 deletions base/vulkanexamplebase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ std::string VulkanExampleBase::getWindowTitle()
return windowTitle;
}

#if !(defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
// iOS & macOS: VulkanExampleBase::getAssetPath() implemented externally to allow access to Objective-C components
const std::string VulkanExampleBase::getAssetPath()
{
#if defined(__ANDROID__)
Expand All @@ -81,6 +83,7 @@ const std::string VulkanExampleBase::getAssetPath()
return "./../data/";
#endif
}
#endif

bool VulkanExampleBase::checkCommandBuffers()
{
Expand Down Expand Up @@ -215,6 +218,45 @@ VkPipelineShaderStageCreateInfo VulkanExampleBase::loadShader(std::string fileNa
return shaderStage;
}

void VulkanExampleBase::renderFrame()
{
#if (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
auto tStart = std::chrono::high_resolution_clock::now();
if (viewUpdated)
{
viewUpdated = false;
viewChanged();
}
render();
frameCounter++;
auto tEnd = std::chrono::high_resolution_clock::now();
auto tDiff = std::chrono::duration<double, std::milli>(tEnd - tStart).count();
frameTimer = tDiff / 1000.0f;
camera.update(frameTimer);
if (camera.moving())
{
viewUpdated = true;
}
// Convert to clamped timer value
if (!paused)
{
timer += timerSpeed * frameTimer;
if (timer > 1.0)
{
timer -= 1.0f;
}
}
fpsTimer += (float)tDiff;
if (fpsTimer > 1000.0f)
{
lastFPS = frameCounter;
updateTextOverlay();
fpsTimer = 0.0f;
frameCounter = 0;
}
#endif
}

void VulkanExampleBase::renderLoop()
{
destWidth = width;
Expand Down Expand Up @@ -1332,6 +1374,12 @@ void VulkanExampleBase::handleAppCommand(android_app * app, int32_t cmd)
break;
}
}
#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
void* VulkanExampleBase::setupWindow(void* view)
{
this->view = view;
return view;
}
#elif defined(_DIRECT2DISPLAY)
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
/*static*/void VulkanExampleBase::registryGlobalCb(void *data,
Expand Down Expand Up @@ -2096,6 +2144,8 @@ void VulkanExampleBase::initSwapchain()
swapChain.initSurface(windowInstance, window);
#elif defined(__ANDROID__)
swapChain.initSurface(androidApp->window);
#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
swapChain.initSurface(view);
#elif defined(_DIRECT2DISPLAY)
swapChain.initSurface(width, height);
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
Expand Down
9 changes: 9 additions & 0 deletions base/vulkanexamplebase.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ class VulkanExampleBase
int64_t lastTapTime = 0;
/** @brief Product model and manufacturer of the Android device (via android.Product*) */
std::string androidProduct;
#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
void* view;
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
wl_display *display = nullptr;
wl_registry *registry = nullptr;
Expand Down Expand Up @@ -256,6 +258,8 @@ class VulkanExampleBase
#elif defined(__ANDROID__)
static int32_t handleAppInput(struct android_app* app, AInputEvent* event);
static void handleAppCommand(android_app* app, int32_t cmd);
#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
void* setupWindow(void* view);
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
wl_shell_surface *setupWindow();
void initWaylandConnection();
Expand Down Expand Up @@ -375,6 +379,9 @@ class VulkanExampleBase
// Start the main render loop
void renderLoop();

// Render one frame of a render loop on platforms that sync rendering
void renderFrame();

void updateTextOverlay();

/** @brief (Virtual) Called when the text overlay is updating, can be used to add custom text to the overlay */
Expand Down Expand Up @@ -489,4 +496,6 @@ int main(const int argc, const char *argv[]) \
delete(vulkanExample); \
return 0; \
}
#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
#define VULKAN_EXAMPLE_MAIN()
#endif
2 changes: 1 addition & 1 deletion hdr/hdr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ class VulkanExample : public VulkanExampleBase
std::vector<std::string> filenames = { "geosphere.obj", "teapot.dae", "torusknot.obj", "venus.fbx" };
for (auto file : filenames) {
vks::Model model;
model.loadFromFile(ASSET_PATH "models/" + file, vertexLayout, 0.05f * (file == "venus.fbx" ? 3.0f : 1.0f), vulkanDevice, queue);
model.loadFromFile(getAssetPath() + "models/" + file, vertexLayout, 0.05f * (file == "venus.fbx" ? 3.0f : 1.0f), vulkanDevice, queue);
models.objects.push_back(model);
}
// Load HDR cube map
Expand Down
Binary file added images/applelogo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/vulkanlogo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 26 additions & 9 deletions multisampling/multisampling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <vector>
#include <algorithm>

#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
Expand All @@ -25,7 +25,6 @@

#define VERTEX_BUFFER_BIND_ID 0
#define ENABLE_VALIDATION false
#define SAMPLE_COUNT VK_SAMPLE_COUNT_8_BIT

struct {
struct {
Expand All @@ -44,6 +43,7 @@ class VulkanExample : public VulkanExampleBase
{
public:
bool useSampleShading = false;
VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT;

struct {
vks::Texture2D colorMap;
Expand Down Expand Up @@ -123,7 +123,7 @@ class VulkanExample : public VulkanExampleBase
void setupMultisampleTarget()
{
// Check if device supports requested sample count for color and depth frame buffer
assert((deviceProperties.limits.framebufferColorSampleCounts >= SAMPLE_COUNT) && (deviceProperties.limits.framebufferDepthSampleCounts >= SAMPLE_COUNT));
assert((deviceProperties.limits.framebufferColorSampleCounts >= sampleCount) && (deviceProperties.limits.framebufferDepthSampleCounts >= sampleCount));

// Color target
VkImageCreateInfo info = vks::initializers::imageCreateInfo();
Expand All @@ -136,7 +136,7 @@ class VulkanExample : public VulkanExampleBase
info.arrayLayers = 1;
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
info.tiling = VK_IMAGE_TILING_OPTIMAL;
info.samples = SAMPLE_COUNT;
info.samples = sampleCount;
// Image will only be used as a transient target
info.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
Expand Down Expand Up @@ -184,7 +184,7 @@ class VulkanExample : public VulkanExampleBase
info.arrayLayers = 1;
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
info.tiling = VK_IMAGE_TILING_OPTIMAL;
info.samples = SAMPLE_COUNT;
info.samples = sampleCount;
// Image will only be used as a transient target
info.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
Expand Down Expand Up @@ -230,7 +230,7 @@ class VulkanExample : public VulkanExampleBase

// Multisampled attachment that we render to
attachments[0].format = swapChain.colorFormat;
attachments[0].samples = SAMPLE_COUNT;
attachments[0].samples = sampleCount;
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
// No longer required after resolve, this may save some bandwidth on certain GPUs
attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
Expand All @@ -252,7 +252,7 @@ class VulkanExample : public VulkanExampleBase

// Multisampled depth attachment we render to
attachments[2].format = depthFormat;
attachments[2].samples = SAMPLE_COUNT;
attachments[2].samples = sampleCount;
attachments[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachments[2].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
Expand Down Expand Up @@ -630,7 +630,7 @@ class VulkanExample : public VulkanExampleBase
shaderStages[0] = loadShader(getAssetPath() + "shaders/mesh/mesh.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getAssetPath() + "shaders/mesh/mesh.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
// Setup multi sampling
multisampleState.rasterizationSamples = SAMPLE_COUNT; // Number of samples to use for rasterization
multisampleState.rasterizationSamples = sampleCount; // Number of samples to use for rasterization

VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.MSAA));

Expand Down Expand Up @@ -693,6 +693,7 @@ class VulkanExample : public VulkanExampleBase

void prepare()
{
sampleCount = getMaxUsableSampleCount();
VulkanExampleBase::prepare();
loadAssets();
setupVertexDescriptions();
Expand Down Expand Up @@ -733,6 +734,22 @@ class VulkanExample : public VulkanExampleBase
break;
}
}

// Returns the maximum sample count usable by the platform
VkSampleCountFlagBits getMaxUsableSampleCount()
{
VkSampleCountFlags counts = std::min(deviceProperties.limits.framebufferColorSampleCounts,
deviceProperties.limits.framebufferDepthSampleCounts);

if (counts & VK_SAMPLE_COUNT_64_BIT) { return VK_SAMPLE_COUNT_64_BIT; }
if (counts & VK_SAMPLE_COUNT_32_BIT) { return VK_SAMPLE_COUNT_32_BIT; }
if (counts & VK_SAMPLE_COUNT_16_BIT) { return VK_SAMPLE_COUNT_16_BIT; }
if (counts & VK_SAMPLE_COUNT_8_BIT) { return VK_SAMPLE_COUNT_8_BIT; }
if (counts & VK_SAMPLE_COUNT_4_BIT) { return VK_SAMPLE_COUNT_4_BIT; }
if (counts & VK_SAMPLE_COUNT_2_BIT) { return VK_SAMPLE_COUNT_2_BIT; }
return VK_SAMPLE_COUNT_1_BIT;
}

};

VULKAN_EXAMPLE_MAIN()
VULKAN_EXAMPLE_MAIN()
2 changes: 1 addition & 1 deletion texture/texture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ class VulkanExample : public VulkanExampleBase
void loadTexture()
{
// We use the Khronos texture format (https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/)
std::string filename = ASSET_PATH "textures/metalplate01_rgba.ktx";
std::string filename = getAssetPath() + "textures/metalplate01_rgba.ktx";
// Texture data contains 4 channels (RGBA) with unnormalized 8-bit values, this is the most commonly supported format
VkFormat format = VK_FORMAT_R8G8B8A8_UNORM;

Expand Down
Loading

0 comments on commit b1da3e2

Please sign in to comment.