1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-26 04:32:35 +01:00

Merge pull request #1548 from kd-11/vulkan-latest-wip

Add vulkan backend
This commit is contained in:
vlj 2016-03-11 00:08:32 +01:00
commit 01abb255b2
28 changed files with 6722 additions and 199 deletions

View File

@ -173,6 +173,16 @@ RPCS3_SRC
"${RPCS3_SRC_DIR}/../rsx_program_decompiler/shader_code/*"
)
if(APPLE)
set (EXCLUDE_DIR "/RSX/VK/")
foreach (TMP_PATH ${RPCS3_SRC})
string (FIND ${TMP_PATH} ${EXCLUDE_DIR} EXCLUDE_DIR_FOUND)
if (NOT ${EXCLUDE_DIR_FOUND} EQUAL -1)
list (REMOVE_ITEM RPCS3_SRC ${TMP_PATH})
endif ()
endforeach(TMP_PATH)
endif()
add_executable(rpcs3 ${RPCS3_SRC})
@ -191,17 +201,22 @@ if(WIN32) # I'm not sure we need all of these libs, but we link them in vs
else()
target_link_libraries(rpcs3 dxgi.lib d2d1.lib dwrite.lib)
endif()
target_link_libraries(rpcs3 asmjit.lib avformat.lib avcodec.lib avutil.lib swresample.lib swscale.lib png16_static ${wxWidgets_LIBRARIES} ${OPENAL_LIBRARY} ${ADDITIONAL_LIBS} ${vulkan} ${glslang})
target_link_libraries(rpcs3 asmjit.lib avformat.lib avcodec.lib avutil.lib swresample.lib swscale.lib png16_static ${wxWidgets_LIBRARIES} ${OPENAL_LIBRARY} ${ADDITIONAL_LIBS} VKstatic.1 glslang OSDependent OGLCompiler SPIRV)
else()
if(LLVM_FOUND)
target_link_libraries(rpcs3 asmjit.a ${wxWidgets_LIBRARIES} ${OPENAL_LIBRARY} ${GLEW_LIBRARY} ${OPENGL_LIBRARIES})
target_link_libraries(rpcs3 libavformat.a libavcodec.a libavutil.a libswresample.a libswscale.a png16_static ${ZLIB_LIBRARIES} ${LLVM_LIBS} ${ADDITIONAL_LIBS} ${vulkan} ${glslang})
target_link_libraries(rpcs3 libavformat.a libavcodec.a libavutil.a libswresample.a libswscale.a png16_static ${ZLIB_LIBRARIES} ${LLVM_LIBS} ${ADDITIONAL_LIBS})
if (NOT APPLE)
target_link_libraries(rpcs3 vulkan glslang OSDependent OGLCompiler SPIRV)
endif()
else()
target_link_libraries(rpcs3 asmjit.a ${wxWidgets_LIBRARIES} ${OPENAL_LIBRARY} ${GLEW_LIBRARY} ${OPENGL_LIBRARIES})
target_link_libraries(rpcs3 libavformat.a libavcodec.a libavutil.a libswresample.a libswscale.a png16_static ${ZLIB_LIBRARIES} ${ADDITIONAL_LIBS} ${vulkan} ${glslang})
target_link_libraries(rpcs3 libavformat.a libavcodec.a libavutil.a libswresample.a libswscale.a png16_static ${ZLIB_LIBRARIES} ${ADDITIONAL_LIBS})
if (NOT APPLE)
target_link_libraries(rpcs3 vulkan glslang OSDependent OGLCompiler SPIRV)
endif()
endif()
endif()
set_target_properties(rpcs3 PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "${RPCS3_SRC_DIR}/stdafx.h")
cotire(rpcs3)
cotire(rpcs3)

View File

@ -57,26 +57,26 @@ struct copy_unmodified_block_swizzled
};
/**
* Texture upload template.
*
* Source textures are stored as following (for power of 2 textures):
* - For linear texture every mipmap level share rowpitch (which is the one of mipmap 0). This means that for non 0 mipmap there's padding between row.
* - For swizzled texture row pitch is texture width X pixel/block size. There's not padding between row.
* - There is no padding between 2 mipmap levels. This means that next mipmap level starts at offset rowpitch X row count
* - Cubemap images are 128 bytes aligned.
*
* The template iterates over all depth (including cubemap) and over all mipmaps.
* The alignment is 256 for mipmap levels and 512 for depth (TODO: make this customisable for Vulkan ?)
* The template takes a struct with a "copy_mipmap_level" static function that copy the given mipmap level and returns the offset to add to the src buffer for next
* mipmap level (to allow same code for packed/non packed texels)
* Sometimes texture provides a pitch even if texture is swizzled (and then packed) and in such case it's ignored. It's passed via suggested_pitch and is used only if padded_row is false.
*/
* Texture upload template.
*
* Source textures are stored as following (for power of 2 textures):
* - For linear texture every mipmap level share rowpitch (which is the one of mipmap 0). This means that for non 0 mipmap there's padding between row.
* - For swizzled texture row pitch is texture width X pixel/block size. There's not padding between row.
* - There is no padding between 2 mipmap levels. This means that next mipmap level starts at offset rowpitch X row count
* - Cubemap images are 128 bytes aligned.
*
* The template iterates over all depth (including cubemap) and over all mipmaps.
* The alignment is 256 for mipmap levels and 512 for depth (DX12), varies for vulkan
* The template takes a struct with a "copy_mipmap_level" static function that copy the given mipmap level and returns the offset to add to the src buffer for next
* mipmap level (to allow same code for packed/non packed texels)
* Sometimes texture provides a pitch even if texture is swizzled (and then packed) and in such case it's ignored. It's passed via suggested_pitch and is used only if padded_row is false.
*/
template <typename T, bool padded_row, u8 block_edge_in_texel, typename DST_TYPE, typename SRC_TYPE>
std::vector<MipmapLevelInfo> copy_texture_data(gsl::span<DST_TYPE> dst, const SRC_TYPE *src, u16 width_in_texel, u16 height_in_texel, u16 depth, u8 layer_count, u16 mipmap_count, u32 suggested_pitch_in_bytes)
std::vector<MipmapLevelInfo> copy_texture_data(gsl::span<DST_TYPE> dst, const SRC_TYPE *src, u16 width_in_texel, u16 height_in_texel, u16 depth, u8 layer_count, u16 mipmap_count, u32 suggested_pitch_in_bytes, size_t alignment)
{
/**
* Note about size type: RSX texture width is stored in a 16 bits int and pitch is stored in a 20 bits int.
*/
* Note about size type: RSX texture width is stored in a 16 bits int and pitch is stored in a 20 bits int.
*/
// <= 128 so fits in u8
u8 block_size_in_bytes = sizeof(DST_TYPE);
@ -92,7 +92,7 @@ std::vector<MipmapLevelInfo> copy_texture_data(gsl::span<DST_TYPE> dst, const SR
for (unsigned mip_level = 0; mip_level < mipmap_count; mip_level++)
{
// since mip_level is up to 16 bits needs at least 17 bits.
u32 dst_pitch = align(miplevel_width_in_block * block_size_in_bytes, 256) / block_size_in_bytes;
u32 dst_pitch = align(miplevel_width_in_block * block_size_in_bytes, alignment) / block_size_in_bytes;
MipmapLevelInfo currentMipmapLevelInfo = {};
currentMipmapLevelInfo.offset = offsetInDst;
@ -118,6 +118,44 @@ std::vector<MipmapLevelInfo> copy_texture_data(gsl::span<DST_TYPE> dst, const SR
return Result;
}
/**
* Copy a single mipmap level starting at a given offset with a given rowpitch alignment
*/
template <typename T, bool padded_row, u8 block_edge_in_texel, typename DST_TYPE, typename SRC_TYPE>
void copy_single_mipmap_layer(gsl::span<DST_TYPE> dst, const SRC_TYPE *src, u16 width_in_texel, u16 height_in_texel, u16 depth, u8 layer_count, u16 mipmap_count, u16 mipmap_index, u16 layer_index, u32 suggested_pitch_in_bytes, u32 dst_pitch)
{
u8 block_size_in_bytes = sizeof(DST_TYPE);
size_t offsetInSrc = 0;
u16 texture_height_in_block = (height_in_texel + block_edge_in_texel - 1) / block_edge_in_texel;
u16 texture_width_in_block = (width_in_texel + block_edge_in_texel - 1) / block_edge_in_texel;
for (unsigned layer = 0; layer <= layer_index; layer++)
{
u16 miplevel_height_in_block = texture_height_in_block, miplevel_width_in_block = texture_width_in_block;
for (unsigned mip_level = 0; mip_level < mipmap_count; mip_level++)
{
u32 src_pitch_in_block = padded_row ? suggested_pitch_in_bytes / block_size_in_bytes : miplevel_width_in_block;
u32 dst_pitch_in_block = dst_pitch / block_size_in_bytes;
const SRC_TYPE *src_with_offset = reinterpret_cast<const SRC_TYPE*>(reinterpret_cast<const char*>(src) + offsetInSrc);
if (mip_level == mipmap_index &&
layer == layer_index)
{
T::copy_mipmap_level(dst.subspan(0, dst_pitch_in_block * depth * miplevel_height_in_block), src_with_offset, miplevel_height_in_block, miplevel_width_in_block, depth, dst_pitch_in_block, src_pitch_in_block);
break;
}
offsetInSrc += miplevel_height_in_block * src_pitch_in_block * block_size_in_bytes * depth;
miplevel_height_in_block = MAX2(miplevel_height_in_block / 2, 1);
miplevel_width_in_block = MAX2(miplevel_width_in_block / 2, 1);
}
offsetInSrc = align(offsetInSrc, 128);
}
}
/**
* A texture is stored as an array of blocks, where a block is a pixel for standard texture
* but is a structure containing several pixels for compressed format
@ -202,7 +240,7 @@ size_t get_texture_block_edge(u32 format)
}
size_t get_placed_texture_storage_size(const rsx::texture &texture, size_t rowPitchAlignement)
size_t get_placed_texture_storage_size(const rsx::texture &texture, size_t rowPitchAlignement, size_t mipmapAlignment)
{
size_t w = texture.width(), h = texture.height(), d = MAX2(texture.depth(), 1);
@ -218,7 +256,7 @@ size_t get_placed_texture_storage_size(const rsx::texture &texture, size_t rowPi
for (unsigned mipmap = 0; mipmap < texture.mipmap(); ++mipmap)
{
size_t rowPitch = align(blockSizeInByte * widthInBlocks, rowPitchAlignement);
result += align(rowPitch * heightInBlocks * d, 512);
result += align(rowPitch * heightInBlocks * d, mipmapAlignment);
heightInBlocks = MAX2(heightInBlocks / 2, 1);
widthInBlocks = MAX2(widthInBlocks / 2, 1);
}
@ -226,7 +264,7 @@ size_t get_placed_texture_storage_size(const rsx::texture &texture, size_t rowPi
return result * (texture.cubemap() ? 6 : 1);
}
std::vector<MipmapLevelInfo> upload_placed_texture(gsl::span<gsl::byte> mapped_buffer, const rsx::texture &texture, size_t rowPitchAlignement)
std::vector<MipmapLevelInfo> upload_placed_texture(gsl::span<gsl::byte> mapped_buffer, const rsx::texture &texture, size_t rowPitchAlignment)
{
u16 w = texture.width(), h = texture.height();
u16 depth;
@ -262,45 +300,132 @@ std::vector<MipmapLevelInfo> upload_placed_texture(gsl::span<gsl::byte> mapped_b
{
case CELL_GCM_TEXTURE_A8R8G8B8:
if (is_swizzled)
return copy_texture_data<copy_unmodified_block_swizzled, false, 1>(as_span_workaround<u32>(mapped_buffer), reinterpret_cast<const u32*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch());
return copy_texture_data<copy_unmodified_block_swizzled, false, 1>(as_span_workaround<u32>(mapped_buffer), reinterpret_cast<const u32*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch(), rowPitchAlignment);
else
return copy_texture_data<copy_unmodified_block, true, 1>(as_span_workaround<u32>(mapped_buffer), reinterpret_cast<const u32*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch());
return copy_texture_data<copy_unmodified_block, true, 1>(as_span_workaround<u32>(mapped_buffer), reinterpret_cast<const u32*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch(), rowPitchAlignment);
case CELL_GCM_TEXTURE_DEPTH16:
case CELL_GCM_TEXTURE_A1R5G5B5:
case CELL_GCM_TEXTURE_A4R4G4B4:
case CELL_GCM_TEXTURE_R5G6B5:
if (is_swizzled)
return copy_texture_data<copy_unmodified_block_swizzled, false, 1>(as_span_workaround<u16>(mapped_buffer), reinterpret_cast<const be_t<u16>*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch());
return copy_texture_data<copy_unmodified_block_swizzled, false, 1>(as_span_workaround<u16>(mapped_buffer), reinterpret_cast<const be_t<u16>*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch(), rowPitchAlignment);
else
return copy_texture_data<copy_unmodified_block, true, 1>(as_span_workaround<u16>(mapped_buffer), reinterpret_cast<const be_t<u16>*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch());
return copy_texture_data<copy_unmodified_block, true, 1>(as_span_workaround<u16>(mapped_buffer), reinterpret_cast<const be_t<u16>*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch(), rowPitchAlignment);
case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT:
return copy_texture_data<copy_unmodified_block, true, 1>(as_span_workaround<u16>(mapped_buffer), reinterpret_cast<const be_t<u16>*>(pixels), 4 * w, h, depth, layer, texture.mipmap(), texture.pitch());
return copy_texture_data<copy_unmodified_block, true, 1>(as_span_workaround<u16>(mapped_buffer), reinterpret_cast<const be_t<u16>*>(pixels), 4 * w, h, depth, layer, texture.mipmap(), texture.pitch(), rowPitchAlignment);
case CELL_GCM_TEXTURE_COMPRESSED_DXT1:
if (is_swizzled)
return copy_texture_data<copy_unmodified_block, false, 4>(as_span_workaround<u64>(mapped_buffer), reinterpret_cast<const u64*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch());
return copy_texture_data<copy_unmodified_block, false, 4>(as_span_workaround<u64>(mapped_buffer), reinterpret_cast<const u64*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch(), rowPitchAlignment);
else
return copy_texture_data<copy_unmodified_block, true, 4>(as_span_workaround<u64>(mapped_buffer), reinterpret_cast<const u64*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch());
return copy_texture_data<copy_unmodified_block, true, 4>(as_span_workaround<u64>(mapped_buffer), reinterpret_cast<const u64*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch(), rowPitchAlignment);
case CELL_GCM_TEXTURE_COMPRESSED_DXT23:
if (is_swizzled)
return copy_texture_data<copy_unmodified_block, false, 4>(as_span_workaround<u128>(mapped_buffer), reinterpret_cast<const u128*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch());
return copy_texture_data<copy_unmodified_block, false, 4>(as_span_workaround<u128>(mapped_buffer), reinterpret_cast<const u128*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch(), rowPitchAlignment);
else
return copy_texture_data<copy_unmodified_block, true, 4>(as_span_workaround<u128>(mapped_buffer), reinterpret_cast<const u128*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch());
return copy_texture_data<copy_unmodified_block, true, 4>(as_span_workaround<u128>(mapped_buffer), reinterpret_cast<const u128*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch(), rowPitchAlignment);
case CELL_GCM_TEXTURE_COMPRESSED_DXT45:
if (is_swizzled)
return copy_texture_data<copy_unmodified_block, false, 4>(as_span_workaround<u128>(mapped_buffer), reinterpret_cast<const u128*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch());
return copy_texture_data<copy_unmodified_block, false, 4>(as_span_workaround<u128>(mapped_buffer), reinterpret_cast<const u128*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch(), rowPitchAlignment);
else
return copy_texture_data<copy_unmodified_block, true, 4>(as_span_workaround<u128>(mapped_buffer), reinterpret_cast<const u128*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch());
return copy_texture_data<copy_unmodified_block, true, 4>(as_span_workaround<u128>(mapped_buffer), reinterpret_cast<const u128*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch(), rowPitchAlignment);
case CELL_GCM_TEXTURE_B8:
if (is_swizzled)
return copy_texture_data<copy_unmodified_block_swizzled, false, 1>(as_span_workaround<u8>(mapped_buffer), reinterpret_cast<const u8*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch());
return copy_texture_data<copy_unmodified_block_swizzled, false, 1>(as_span_workaround<u8>(mapped_buffer), reinterpret_cast<const u8*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch(), rowPitchAlignment);
else
return copy_texture_data<copy_unmodified_block, true, 1>(as_span_workaround<u8>(mapped_buffer), reinterpret_cast<const u8*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch());
case CELL_GCM_TEXTURE_DEPTH24_D8: // Opaque type ; ATM do not copy anything
return std::vector<MipmapLevelInfo>();
return copy_texture_data<copy_unmodified_block, true, 1>(as_span_workaround<u8>(mapped_buffer), reinterpret_cast<const u8*>(pixels), w, h, depth, layer, texture.mipmap(), texture.pitch(), rowPitchAlignment);
}
throw EXCEPTION("Wrong format %d", format);
}
/**
* Upload texture mipmaps where alignment and offset information is provided manually
*/
void upload_texture_mipmaps(gsl::span<gsl::byte> dst_buffer, const rsx::texture &texture, std::vector<std::pair<u64, u32>> alignment_offset_info)
{
u16 w = texture.width(), h = texture.height();
u16 depth;
u8 layer;
if (texture.dimension() == 1)
{
depth = 1;
layer = 1;
h = 1;
}
else if (texture.dimension() == 2)
{
depth = 1;
layer = texture.cubemap() ? 6 : 1;
}
else if (texture.dimension() == 3)
{
depth = texture.depth();
layer = 1;
}
else
throw EXCEPTION("Unsupported texture dimension %d", texture.dimension());
int format = texture.format() & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN);
const u32 texaddr = rsx::get_address(texture.offset(), texture.location());
auto pixels = vm::ps3::_ptr<const u8>(texaddr);
bool is_swizzled = !(texture.format() & CELL_GCM_TEXTURE_LN);
//TODO: Layers greater than 0
for (u32 mip_level = 0; mip_level < texture.mipmap(); ++mip_level)
{
gsl::span<gsl::byte> mapped_buffer = dst_buffer.subspan(alignment_offset_info[mip_level].first);
switch (format)
{
case CELL_GCM_TEXTURE_A8R8G8B8:
if (is_swizzled)
copy_single_mipmap_layer<copy_unmodified_block_swizzled, false, 1>(as_span_workaround<u32>(mapped_buffer), reinterpret_cast<const u32*>(pixels), w, h, depth, layer, texture.mipmap(), mip_level, 0, texture.pitch(), alignment_offset_info[mip_level].second);
else
copy_single_mipmap_layer<copy_unmodified_block, true, 1>(as_span_workaround<u32>(mapped_buffer), reinterpret_cast<const u32*>(pixels), w, h, depth, layer, texture.mipmap(), mip_level, 0, texture.pitch(), alignment_offset_info[mip_level].second);
break;
case CELL_GCM_TEXTURE_DEPTH16:
case CELL_GCM_TEXTURE_A1R5G5B5:
case CELL_GCM_TEXTURE_A4R4G4B4:
case CELL_GCM_TEXTURE_R5G6B5:
if (is_swizzled)
copy_single_mipmap_layer<copy_unmodified_block_swizzled, false, 1>(as_span_workaround<u16>(mapped_buffer), reinterpret_cast<const be_t<u16>*>(pixels), w, h, depth, layer, texture.mipmap(), mip_level, 0, texture.pitch(), alignment_offset_info[mip_level].second);
else
copy_single_mipmap_layer<copy_unmodified_block, true, 1>(as_span_workaround<u16>(mapped_buffer), reinterpret_cast<const be_t<u16>*>(pixels), w, h, depth, layer, texture.mipmap(), mip_level, 0, texture.pitch(), alignment_offset_info[mip_level].second);
break;
case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT:
copy_single_mipmap_layer<copy_unmodified_block, true, 1>(as_span_workaround<u16>(mapped_buffer), reinterpret_cast<const be_t<u16>*>(pixels), w, h, depth, layer, texture.mipmap(), mip_level, 0, texture.pitch(), alignment_offset_info[mip_level].second);
break;
case CELL_GCM_TEXTURE_COMPRESSED_DXT1:
if (is_swizzled)
copy_single_mipmap_layer<copy_unmodified_block, false, 4>(as_span_workaround<u64>(mapped_buffer), reinterpret_cast<const u64*>(pixels), w, h, depth, layer, texture.mipmap(), mip_level, 0, texture.pitch(), alignment_offset_info[mip_level].second);
else
copy_single_mipmap_layer<copy_unmodified_block, true, 4>(as_span_workaround<u64>(mapped_buffer), reinterpret_cast<const u64*>(pixels), w, h, depth, layer, texture.mipmap(), mip_level, 0, texture.pitch(), alignment_offset_info[mip_level].second);
break;
case CELL_GCM_TEXTURE_COMPRESSED_DXT23:
if (is_swizzled)
copy_single_mipmap_layer<copy_unmodified_block, false, 4>(as_span_workaround<u128>(mapped_buffer), reinterpret_cast<const u128*>(pixels), w, h, depth, layer, texture.mipmap(), mip_level, 0, texture.pitch(), alignment_offset_info[mip_level].second);
else
copy_single_mipmap_layer<copy_unmodified_block, true, 4>(as_span_workaround<u128>(mapped_buffer), reinterpret_cast<const u128*>(pixels), w, h, depth, layer, texture.mipmap(), mip_level, 0, texture.pitch(), alignment_offset_info[mip_level].second);
break;
case CELL_GCM_TEXTURE_COMPRESSED_DXT45:
if (is_swizzled)
copy_single_mipmap_layer<copy_unmodified_block, false, 4>(as_span_workaround<u128>(mapped_buffer), reinterpret_cast<const u128*>(pixels), w, h, depth, layer, texture.mipmap(), mip_level, 0, texture.pitch(), alignment_offset_info[mip_level].second);
else
copy_single_mipmap_layer<copy_unmodified_block, true, 4>(as_span_workaround<u128>(mapped_buffer), reinterpret_cast<const u128*>(pixels), w, h, depth, layer, texture.mipmap(), mip_level, 0, texture.pitch(), alignment_offset_info[mip_level].second);
break;
case CELL_GCM_TEXTURE_B8:
if (is_swizzled)
copy_single_mipmap_layer<copy_unmodified_block_swizzled, false, 1>(as_span_workaround<u8>(mapped_buffer), reinterpret_cast<const u8*>(pixels), w, h, depth, layer, texture.mipmap(), mip_level, 0, texture.pitch(), alignment_offset_info[mip_level].second);
else
copy_single_mipmap_layer<copy_unmodified_block, true, 1>(as_span_workaround<u8>(mapped_buffer), reinterpret_cast<const u8*>(pixels), w, h, depth, layer, texture.mipmap(), mip_level, 0, texture.pitch(), alignment_offset_info[mip_level].second);
break;
default:
throw EXCEPTION("Wrong format %d", format);
}
}
}
size_t get_texture_size(const rsx::texture &texture)
{
size_t w = texture.width(), h = texture.height();

View File

@ -15,7 +15,7 @@ struct MipmapLevelInfo
* Get size to store texture in a linear fashion.
* Storage is assumed to use a rowPitchAlignement boundary for every row of texture.
*/
size_t get_placed_texture_storage_size(const rsx::texture &texture, size_t rowPitchAlignement);
size_t get_placed_texture_storage_size(const rsx::texture &texture, size_t rowPitchAlignement, size_t mipmapAlignment=512);
/**
* Write texture data to textureData.
@ -24,6 +24,13 @@ size_t get_placed_texture_storage_size(const rsx::texture &texture, size_t rowPi
*/
std::vector<MipmapLevelInfo> upload_placed_texture(gsl::span<gsl::byte> mapped_buffer, const rsx::texture &texture, size_t rowPitchAlignement);
/**
* Upload texture mipmaps where alignment and offset information is provided manually.
* alignment_offset info is an array of N mipmaps providing the offset into the data block and row-pitch alignment of each
* mipmap level individually.
*/
void upload_texture_mipmaps(gsl::span<gsl::byte> dst_buffer, const rsx::texture &texture, std::vector<std::pair<u64, u32>> alignment_offset_info);
/**
* Get number of bytes occupied by texture in RSX mem
*/

View File

@ -36,7 +36,8 @@ enum class frame_type
{
Null,
OpenGL,
DX12
DX12,
Vulkan
};
class GSRender : public rsx::thread

View File

@ -0,0 +1,291 @@
#include "stdafx.h"
#include "VKCommonDecompiler.h"
#include "../../../../Vulkan/glslang/SPIRV/GlslangToSpv.h"
namespace vk
{
std::string getFloatTypeNameImpl(size_t elementCount)
{
switch (elementCount)
{
default:
abort();
case 1:
return "float";
case 2:
return "vec2";
case 3:
return "vec3";
case 4:
return "vec4";
}
}
std::string getFunctionImpl(FUNCTION f)
{
switch (f)
{
default:
abort();
case FUNCTION::FUNCTION_DP2:
return "vec4(dot($0.xy, $1.xy))";
case FUNCTION::FUNCTION_DP2A:
return "";
case FUNCTION::FUNCTION_DP3:
return "vec4(dot($0.xyz, $1.xyz))";
case FUNCTION::FUNCTION_DP4:
return "vec4(dot($0, $1))";
case FUNCTION::FUNCTION_DPH:
return "vec4(dot(vec4($0.xyz, 1.0), $1))";
case FUNCTION::FUNCTION_SFL:
return "vec4(0., 0., 0., 0.)";
case FUNCTION::FUNCTION_STR:
return "vec4(1., 1., 1., 1.)";
case FUNCTION::FUNCTION_FRACT:
return "fract($0)";
case FUNCTION::FUNCTION_TEXTURE_SAMPLE1D:
return "texture($t, $0.x)";
case FUNCTION::FUNCTION_TEXTURE_SAMPLE1D_PROJ:
return "textureProj($t, $0.x, $1.x)"; // Note: $1.x is bias
case FUNCTION::FUNCTION_TEXTURE_SAMPLE1D_LOD:
return "textureLod($t, $0.x, $1)";
case FUNCTION::FUNCTION_TEXTURE_SAMPLE2D:
return "texture($t, $0.xy)";
case FUNCTION::FUNCTION_TEXTURE_SAMPLE2D_PROJ:
return "textureProj($t, $0.xyz, $1.x)"; // Note: $1.x is bias
case FUNCTION::FUNCTION_TEXTURE_SAMPLE2D_LOD:
return "textureLod($t, $0.xy, $1)";
case FUNCTION::FUNCTION_TEXTURE_SAMPLECUBE:
return "texture($t, $0.xyz)";
case FUNCTION::FUNCTION_TEXTURE_SAMPLECUBE_PROJ:
return "textureProj($t, $0.xyzw, $1.x)"; // Note: $1.x is bias
case FUNCTION::FUNCTION_TEXTURE_SAMPLECUBE_LOD:
return "textureLod($t, $0.xyz, $1)";
case FUNCTION::FUNCTION_DFDX:
return "dFdx($0)";
case FUNCTION::FUNCTION_DFDY:
return "dFdy($0)";
}
}
std::string compareFunctionImpl(COMPARE f, const std::string &Op0, const std::string &Op1)
{
switch (f)
{
case COMPARE::FUNCTION_SEQ:
return "equal(" + Op0 + ", " + Op1 + ")";
case COMPARE::FUNCTION_SGE:
return "greaterThanEqual(" + Op0 + ", " + Op1 + ")";
case COMPARE::FUNCTION_SGT:
return "greaterThan(" + Op0 + ", " + Op1 + ")";
case COMPARE::FUNCTION_SLE:
return "lessThanEqual(" + Op0 + ", " + Op1 + ")";
case COMPARE::FUNCTION_SLT:
return "lessThan(" + Op0 + ", " + Op1 + ")";
case COMPARE::FUNCTION_SNE:
return "notEqual(" + Op0 + ", " + Op1 + ")";
}
throw EXCEPTION("Unknow compare function");
}
void insert_glsl_legacy_function(std::ostream& OS)
{
OS << "vec4 divsq_legacy(vec4 num, vec4 denum)\n";
OS << "{\n";
OS << " return num / sqrt(max(denum.xxxx, 1.E-10));\n";
OS << "}\n";
OS << "vec4 rcp_legacy(vec4 denum)\n";
OS << "{\n";
OS << " return 1. / denum;\n";
OS << "}\n";
OS << "vec4 rsq_legacy(vec4 val)\n";
OS << "{\n";
OS << " return float(1.0 / sqrt(max(val.x, 1.E-10))).xxxx;\n";
OS << "}\n\n";
OS << "vec4 log2_legacy(vec4 val)\n";
OS << "{\n";
OS << " return log2(max(val.x, 1.E-10)).xxxx;\n";
OS << "}\n\n";
OS << "vec4 lit_legacy(vec4 val)";
OS << "{\n";
OS << " vec4 clamped_val = val;\n";
OS << " clamped_val.x = max(val.x, 0.);\n";
OS << " clamped_val.y = max(val.y, 0.);\n";
OS << " vec4 result;\n";
OS << " result.x = 1.;\n";
OS << " result.w = 1.;\n";
OS << " result.y = clamped_val.x;\n";
OS << " result.z = clamped_val.x > 0. ? exp(clamped_val.w * log(max(clamped_val.y, 1.E-10))) : 0.;\n";
OS << " return result;\n";
OS << "}\n\n";
}
void init_default_resources(TBuiltInResource &rsc)
{
rsc.maxLights = 32;
rsc.maxClipPlanes = 6;
rsc.maxTextureUnits = 32;
rsc.maxTextureCoords = 32;
rsc.maxVertexAttribs = 64;
rsc.maxVertexUniformComponents = 4096;
rsc.maxVaryingFloats = 64;
rsc.maxVertexTextureImageUnits = 32;
rsc.maxCombinedTextureImageUnits = 80;
rsc.maxTextureImageUnits = 32;
rsc.maxFragmentUniformComponents = 4096;
rsc.maxDrawBuffers = 32;
rsc.maxVertexUniformVectors = 128;
rsc.maxVaryingVectors = 8;
rsc.maxFragmentUniformVectors = 16;
rsc.maxVertexOutputVectors = 16;
rsc.maxFragmentInputVectors = 15;
rsc.maxProgramTexelOffset = -8;
rsc.maxProgramTexelOffset = 7;
rsc.maxClipDistances = 8;
rsc.maxComputeWorkGroupCountX = 65535;
rsc.maxComputeWorkGroupCountY = 65535;
rsc.maxComputeWorkGroupCountZ = 65535;
rsc.maxComputeWorkGroupSizeX = 1024;
rsc.maxComputeWorkGroupSizeY = 1024;
rsc.maxComputeWorkGroupSizeZ = 64;
rsc.maxComputeUniformComponents = 1024;
rsc.maxComputeTextureImageUnits = 16;
rsc.maxComputeImageUniforms = 8;
rsc.maxComputeAtomicCounters = 8;
rsc.maxComputeAtomicCounterBuffers = 1;
rsc.maxVaryingComponents = 60;
rsc.maxVertexOutputComponents = 64;
rsc.maxGeometryInputComponents = 64;
rsc.maxGeometryOutputComponents = 128;
rsc.maxFragmentInputComponents = 128;
rsc.maxImageUnits = 8;
rsc.maxCombinedImageUnitsAndFragmentOutputs = 8;
rsc.maxCombinedShaderOutputResources = 8;
rsc.maxImageSamples = 0;
rsc.maxVertexImageUniforms = 0;
rsc.maxTessControlImageUniforms = 0;
rsc.maxTessEvaluationImageUniforms = 0;
rsc.maxGeometryImageUniforms = 0;
rsc.maxFragmentImageUniforms = 8;
rsc.maxCombinedImageUniforms = 8;
rsc.maxGeometryTextureImageUnits = 16;
rsc.maxGeometryOutputVertices = 256;
rsc.maxGeometryTotalOutputComponents = 1024;
rsc.maxGeometryUniformComponents = 1024;
rsc.maxGeometryVaryingComponents = 64;
rsc.maxTessControlInputComponents = 128;
rsc.maxTessControlOutputComponents = 128;
rsc.maxTessControlTextureImageUnits = 16;
rsc.maxTessControlUniformComponents = 1024;
rsc.maxTessControlTotalOutputComponents = 4096;
rsc.maxTessEvaluationInputComponents = 128;
rsc.maxTessEvaluationOutputComponents = 128;
rsc.maxTessEvaluationTextureImageUnits = 16;
rsc.maxTessEvaluationUniformComponents = 1024;
rsc.maxTessPatchComponents = 120;
rsc.maxPatchVertices = 32;
rsc.maxTessGenLevel = 64;
rsc.maxViewports = 16;
rsc.maxVertexAtomicCounters = 0;
rsc.maxTessControlAtomicCounters = 0;
rsc.maxTessEvaluationAtomicCounters = 0;
rsc.maxGeometryAtomicCounters = 0;
rsc.maxFragmentAtomicCounters = 8;
rsc.maxCombinedAtomicCounters = 8;
rsc.maxAtomicCounterBindings = 1;
rsc.maxVertexAtomicCounterBuffers = 0;
rsc.maxTessControlAtomicCounterBuffers = 0;
rsc.maxTessEvaluationAtomicCounterBuffers = 0;
rsc.maxGeometryAtomicCounterBuffers = 0;
rsc.maxFragmentAtomicCounterBuffers = 1;
rsc.maxCombinedAtomicCounterBuffers = 1;
rsc.maxAtomicCounterBufferSize = 16384;
rsc.maxTransformFeedbackBuffers = 4;
rsc.maxTransformFeedbackInterleavedComponents = 64;
rsc.maxCullDistances = 8;
rsc.maxCombinedClipAndCullDistances = 8;
rsc.maxSamples = 4;
rsc.limits.nonInductiveForLoops = 1;
rsc.limits.whileLoops = 1;
rsc.limits.doWhileLoops = 1;
rsc.limits.generalUniformIndexing = 1;
rsc.limits.generalAttributeMatrixVectorIndexing = 1;
rsc.limits.generalVaryingIndexing = 1;
rsc.limits.generalSamplerIndexing = 1;
rsc.limits.generalVariableIndexing = 1;
rsc.limits.generalConstantMatrixVectorIndexing = 1;
}
static const varying_register_t varying_regs[] =
{
{ "diff_color", 0 },
{ "tc0", 1 },
{ "tc1", 2 },
{ "tc2", 3 },
{ "tc3", 4 },
{ "tc4", 5 },
{ "tc5", 6 },
{ "tc6", 7 },
{ "tc7", 8 },
{ "tc8", 9 },
{ "tc9", 10 },
{ "front_diff_color", 11 },
{ "front_spec_color", 12 },
{ "spec_color", 13 },
{ "fog_c", 14 },
{ "fogc", 14 }
};
const varying_register_t & get_varying_register(const std::string & name)
{
for (const auto&t : varying_regs)
{
if (t.name == name)
return t;
}
throw EXCEPTION("Unknown register name: %s", name);
}
bool compile_glsl_to_spv(std::string& shader, glsl::program_domain domain, std::vector<u32>& spv)
{
EShLanguage lang = (domain == glsl::glsl_fragment_program) ? EShLangFragment : EShLangVertex;
glslang::InitializeProcess();
glslang::TProgram program;
glslang::TShader shader_object(lang);
bool success = false;
const char *shader_text = shader.data();
TBuiltInResource rsc;
init_default_resources(rsc);
shader_object.setStrings(&shader_text, 1);
EShMessages msg = (EShMessages)(EShMsgVulkanRules | EShMsgSpvRules);
if (shader_object.parse(&rsc, 400, EProfile::ECoreProfile, false, true, msg))
{
program.addShader(&shader_object);
success = program.link(EShMsgVulkanRules);
if (success)
{
glslang::TIntermediate* bytes = program.getIntermediate(lang);
glslang::GlslangToSpv(*bytes, spv);
}
}
else
{
LOG_ERROR(RSX, shader_object.getInfoLog());
LOG_ERROR(RSX, shader_object.getInfoDebugLog());
}
glslang::FinalizeProcess();
return success;
}
}

View File

@ -0,0 +1,20 @@
#pragma once
#include "../Common/ShaderParam.h"
#include "VKHelpers.h"
namespace vk
{
struct varying_register_t
{
std::string name;
int reg_location;
};
std::string getFloatTypeNameImpl(size_t elementCount);
std::string getFunctionImpl(FUNCTION f);
std::string compareFunctionImpl(COMPARE f, const std::string &Op0, const std::string &Op1);
void insert_glsl_legacy_function(std::ostream& OS);
const varying_register_t& get_varying_register(const std::string& name);
bool compile_glsl_to_spv(std::string& shader, glsl::program_domain domain, std::vector<u32> &spv);
}

View File

@ -0,0 +1,321 @@
#include "stdafx.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "VKFragmentProgram.h"
#include "VKCommonDecompiler.h"
#include "VKHelpers.h"
#include "../GCM.h"
std::string VKFragmentDecompilerThread::getFloatTypeName(size_t elementCount)
{
return vk::getFloatTypeNameImpl(elementCount);
}
std::string VKFragmentDecompilerThread::getFunction(FUNCTION f)
{
return vk::getFunctionImpl(f);
}
std::string VKFragmentDecompilerThread::saturate(const std::string & code)
{
return "clamp(" + code + ", 0., 1.)";
}
std::string VKFragmentDecompilerThread::compareFunction(COMPARE f, const std::string &Op0, const std::string &Op1)
{
return vk::compareFunctionImpl(f, Op0, Op1);
}
void VKFragmentDecompilerThread::insertHeader(std::stringstream & OS)
{
OS << "#version 420" << std::endl;
OS << "#extension GL_ARB_separate_shader_objects: enable" << std::endl << std::endl;
OS << "layout(std140, set=1, binding = 0) uniform ScaleOffsetBuffer" << std::endl;
OS << "{" << std::endl;
OS << " mat4 scaleOffsetMat;" << std::endl;
OS << " float fog_param0;" << std::endl;
OS << " float fog_param1;" << std::endl;
OS << "};" << std::endl << std::endl;
vk::glsl::program_input in;
in.location = 0;
in.domain = vk::glsl::glsl_fragment_program;
in.name = "ScaleOffsetBuffer";
in.type = vk::glsl::input_type_uniform_buffer;
inputs.push_back(in);
}
void VKFragmentDecompilerThread::insertIntputs(std::stringstream & OS)
{
for (const ParamType& PT : m_parr.params[PF_PARAM_IN])
{
for (const ParamItem& PI : PT.items)
{
const vk::varying_register_t &reg = vk::get_varying_register(PI.name);
std::string var_name = PI.name;
if (var_name == "fogc")
var_name = "fog_c";
OS << "layout(location=" << reg.reg_location << ") in " << PT.type << " " << var_name << ";" << std::endl;
}
}
}
void VKFragmentDecompilerThread::insertOutputs(std::stringstream & OS)
{
const std::pair<std::string, std::string> table[] =
{
{ "ocol0", m_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS ? "r0" : "h0" },
{ "ocol1", m_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS ? "r2" : "h4" },
{ "ocol2", m_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS ? "r3" : "h6" },
{ "ocol3", m_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS ? "r4" : "h8" },
};
for (int i = 0; i < sizeof(table) / sizeof(*table); ++i)
{
if (m_parr.HasParam(PF_PARAM_NONE, "vec4", table[i].second))
OS << "layout(location=" << i << ") " << "out vec4 " << table[i].first << ";" << std::endl;
}
}
void VKFragmentDecompilerThread::insertConstants(std::stringstream & OS)
{
int location = 2;
for (const ParamType& PT : m_parr.params[PF_PARAM_UNIFORM])
{
if (PT.type != "sampler1D" &&
PT.type != "sampler2D" &&
PT.type != "sampler3D" &&
PT.type != "samplerCube")
continue;
for (const ParamItem& PI : PT.items)
{
std::string samplerType = PT.type;
int index = atoi(&PI.name.data()[3]);
if (m_prog.unnormalized_coords & (1 << index))
samplerType = "sampler2DRect";
vk::glsl::program_input in;
in.location = location;
in.domain = vk::glsl::glsl_fragment_program;
in.name = PI.name;
in.type = vk::glsl::input_type_texture;
inputs.push_back(in);
OS << "layout(set=1, binding=" << location++ << ") uniform " << samplerType << " " << PI.name << ";" << std::endl;
}
}
OS << "layout(std140, set=1, binding = 1) uniform FragmentConstantsBuffer" << std::endl;
OS << "{" << std::endl;
for (const ParamType& PT : m_parr.params[PF_PARAM_UNIFORM])
{
if (PT.type == "sampler1D" ||
PT.type == "sampler2D" ||
PT.type == "sampler3D" ||
PT.type == "samplerCube")
continue;
for (const ParamItem& PI : PT.items)
OS << " " << PT.type << " " << PI.name << ";" << std::endl;
}
// A dummy value otherwise it's invalid to create an empty uniform buffer
OS << " vec4 void_value;" << std::endl;
OS << "};" << std::endl;
vk::glsl::program_input in;
in.location = 1;
in.domain = vk::glsl::glsl_fragment_program;
in.name = "FragmentConstantsBuffer";
in.type = vk::glsl::input_type_uniform_buffer;
inputs.push_back(in);
}
namespace vk
{
// Note: It's not clear whether fog is computed per pixel or per vertex.
// But it makes more sense to compute exp of interpoled value than to interpolate exp values.
void insert_fog_declaration(std::stringstream & OS, rsx::fog_mode mode)
{
switch (mode)
{
case rsx::fog_mode::linear:
OS << " vec4 fogc = vec4(fog_param1 * fog_c.x + (fog_param0 - 1.), fog_param1 * fog_c.x + (fog_param0 - 1.), 0., 0.);\n";
return;
case rsx::fog_mode::exponential:
OS << " vec4 fogc = vec4(11.084 * (fog_param1 * fog_c.x + fog_param0 - 1.5), exp(11.084 * (fog_param1 * fog_c.x + fog_param0 - 1.5)), 0., 0.);\n";
return;
case rsx::fog_mode::exponential2:
OS << " vec4 fogc = vec4(4.709 * (fog_param1 * fog_c.x + fog_param0 - 1.5), exp(-pow(4.709 * (fog_param1 * fog_c.x + fog_param0 - 1.5)), 2.), 0., 0.);\n";
return;
case rsx::fog_mode::linear_abs:
OS << " vec4 fogc = vec4(fog_param1 * abs(fog_c.x) + (fog_param0 - 1.), fog_param1 * abs(fog_c.x) + (fog_param0 - 1.), 0., 0.);\n";
return;
case rsx::fog_mode::exponential_abs:
OS << " vec4 fogc = vec4(11.084 * (fog_param1 * abs(fog_c.x) + fog_param0 - 1.5), exp(11.084 * (fog_param1 * abs(fog_c.x) + fog_param0 - 1.5)), 0., 0.);\n";
return;
case rsx::fog_mode::exponential2_abs:
OS << " vec4 fogc = vec4(4.709 * (fog_param1 * abs(fog_c.x) + fog_param0 - 1.5), exp(-pow(4.709 * (fog_param1 * abs(fog_c.x) + fog_param0 - 1.5)), 2.), 0., 0.);\n";
return;
}
}
}
void VKFragmentDecompilerThread::insertMainStart(std::stringstream & OS)
{
vk::insert_glsl_legacy_function(OS);
OS << "void main ()" << std::endl;
OS << "{" << std::endl;
for (const ParamType& PT : m_parr.params[PF_PARAM_NONE])
{
for (const ParamItem& PI : PT.items)
{
OS << " " << PT.type << " " << PI.name;
if (!PI.value.empty())
OS << " = " << PI.value;
OS << ";" << std::endl;
}
}
OS << " vec4 ssa = gl_FrontFacing ? vec4(1.) : vec4(-1.);\n";
// search if there is fogc in inputs
for (const ParamType& PT : m_parr.params[PF_PARAM_IN])
{
for (const ParamItem& PI : PT.items)
{
if (PI.name == "fogc")
{
vk::insert_fog_declaration(OS, m_prog.fog_equation);
return;
}
}
}
}
void VKFragmentDecompilerThread::insertMainEnd(std::stringstream & OS)
{
const std::pair<std::string, std::string> table[] =
{
{ "ocol0", m_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS ? "r0" : "h0" },
{ "ocol1", m_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS ? "r2" : "h4" },
{ "ocol2", m_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS ? "r3" : "h6" },
{ "ocol3", m_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS ? "r4" : "h8" },
};
for (int i = 0; i < sizeof(table) / sizeof(*table); ++i)
{
if (m_parr.HasParam(PF_PARAM_NONE, "vec4", table[i].second))
OS << " " << table[i].first << " = " << table[i].second << ";" << std::endl;
}
if (m_ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT)
{
{
/** Note: Naruto Shippuden : Ultimate Ninja Storm 2 sets CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS in a shader
* but it writes depth in r1.z and not h2.z.
* Maybe there's a different flag for depth ?
*/
//OS << ((m_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS) ? "\tgl_FragDepth = r1.z;\n" : "\tgl_FragDepth = h0.z;\n") << std::endl;
OS << " gl_FragDepth = r1.z;\n";
}
}
OS << "}" << std::endl;
}
void VKFragmentDecompilerThread::Task()
{
m_shader = Decompile();
vk_prog->SetInputs(inputs);
}
VKFragmentProgram::VKFragmentProgram()
{
}
VKFragmentProgram::~VKFragmentProgram()
{
Delete();
}
void VKFragmentProgram::Decompile(const RSXFragmentProgram& prog)
{
u32 size;
VKFragmentDecompilerThread decompiler(shader, parr, prog, size, *this);
decompiler.Task();
for (const ParamType& PT : decompiler.m_parr.params[PF_PARAM_UNIFORM])
{
for (const ParamItem& PI : PT.items)
{
if (PT.type == "sampler2D")
continue;
size_t offset = atoi(PI.name.c_str() + 2);
FragmentConstantOffsetCache.push_back(offset);
}
}
}
void VKFragmentProgram::Compile()
{
fs::file(fs::get_config_dir() + "FragmentProgram.frag", fom::rewrite).write(shader);
std::vector<u32> spir_v;
if (!vk::compile_glsl_to_spv(shader, vk::glsl::glsl_fragment_program, spir_v))
throw EXCEPTION("Failed to compile fragment shader");
//Create the object and compile
VkShaderModuleCreateInfo fs_info;
fs_info.codeSize = spir_v.size() * sizeof(u32);
fs_info.pNext = nullptr;
fs_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
fs_info.pCode = (uint32_t*)spir_v.data();
fs_info.flags = 0;
VkDevice dev = (VkDevice)*vk::get_current_renderer();
vkCreateShaderModule(dev, &fs_info, nullptr, &handle);
id = (u32)((u64)handle);
}
void VKFragmentProgram::Delete()
{
shader.clear();
if (handle)
{
if (Emu.IsStopped())
{
LOG_WARNING(RSX, "VKFragmentProgram::Delete(): vkDestroyShaderModule(0x%X) avoided", handle);
}
else
{
VkDevice dev = (VkDevice)*vk::get_current_renderer();
vkDestroyShaderModule(dev, handle, NULL);
handle = nullptr;
}
}
}
void VKFragmentProgram::SetInputs(std::vector<vk::glsl::program_input>& inputs)
{
for (auto &it : inputs)
{
uniforms.push_back(it);
}
}

View File

@ -0,0 +1,69 @@
#pragma once
#include "../Common/FragmentProgramDecompiler.h"
#include "Emu/RSX/RSXFragmentProgram.h"
#include "Utilities/Thread.h"
#include "VulkanAPI.h"
#include "../VK/VKHelpers.h"
struct VKFragmentDecompilerThread : public FragmentProgramDecompiler
{
std::string& m_shader;
ParamArray& m_parrDummy;
std::vector<vk::glsl::program_input> inputs;
class VKFragmentProgram *vk_prog;
public:
VKFragmentDecompilerThread(std::string& shader, ParamArray& parr, const RSXFragmentProgram &prog, u32& size, class VKFragmentProgram& dst)
: FragmentProgramDecompiler(prog, size)
, m_shader(shader)
, m_parrDummy(parr)
, vk_prog(&dst)
{
}
void Task();
const std::vector<vk::glsl::program_input>& get_inputs() { return inputs; }
protected:
virtual std::string getFloatTypeName(size_t elementCount) override;
virtual std::string getFunction(FUNCTION) override;
virtual std::string saturate(const std::string &code) override;
virtual std::string compareFunction(COMPARE, const std::string&, const std::string&) override;
virtual void insertHeader(std::stringstream &OS) override;
virtual void insertIntputs(std::stringstream &OS) override;
virtual void insertOutputs(std::stringstream &OS) override;
virtual void insertConstants(std::stringstream &OS) override;
virtual void insertMainStart(std::stringstream &OS) override;
virtual void insertMainEnd(std::stringstream &OS) override;
};
/** Storage for an Fragment Program in the process of of recompilation.
* This class calls OpenGL functions and should only be used from the RSX/Graphics thread.
*/
class VKFragmentProgram
{
public:
VKFragmentProgram();
~VKFragmentProgram();
ParamArray parr;
VkShaderModule handle = nullptr;
u32 id;
std::string shader;
std::vector<size_t> FragmentConstantOffsetCache;
std::vector<vk::glsl::program_input> uniforms;
void SetInputs(std::vector<vk::glsl::program_input>& uniforms);
/**
* Decompile a fragment shader located in the PS3's Memory. This function operates synchronously.
* @param prog RSXShaderProgram specifying the location and size of the shader in memory
* @param td texture dimensions of input textures
*/
void Decompile(const RSXFragmentProgram& prog);
/** Compile the decompiled fragment shader into a format we can use with OpenGL. */
void Compile();
private:
/** Deletes the shader and any stored information */
void Delete();
};

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,100 @@
#pragma once
#include <vulkan/vulkan.h>
#include <ShaderLang.h>
#include "Emu/RSX/GSRender.h"
#include "VKHelpers.h"
#include "VKTextureCache.h"
#include "VKRenderTargets.h"
class VKGSRender
#define RSX_DEBUG 1
#include "VKProgramBuffer.h"
#include "../GCM.h"
#pragma comment(lib, "VKstatic.1.lib")
class VKGSRender : public GSRender
{
private:
VKFragmentProgram m_fragment_prog;
VKVertexProgram m_vertex_prog;
vk::glsl::program *m_program;
vk::context m_thread_context;
rsx::surface_info m_surface;
vk::buffer m_attrib_buffers[rsx::limits::vertex_count];
vk::texture_cache m_texture_cache;
rsx::vk_render_targets m_rtts;
public:
//vk::fbo draw_fbo;
private:
VKProgramBuffer m_prog_buffer;
vk::render_device *m_device;
vk::swap_chain* m_swap_chain;
//buffer
vk::buffer m_scale_offset_buffer;
vk::buffer m_vertex_constants_buffer;
vk::buffer m_fragment_constants_buffer;
vk::buffer m_index_buffer;
//Vulkan internals
u32 m_current_present_image = 0xFFFF;
VkSemaphore m_present_semaphore = nullptr;
u32 m_current_sync_buffer_index = 0;
VkFence m_submit_fence = nullptr;
vk::command_pool m_command_buffer_pool;
vk::command_buffer m_command_buffer;
bool recording = false;
bool dirty_frame = true;
//Single render pass
VkRenderPass m_render_pass = nullptr;
u32 m_draw_calls = 0;
u8 m_draw_buffers_count = 0;
vk::framebuffer m_framebuffer;
public:
VKGSRender();
};
~VKGSRender();
private:
void clear_surface(u32 mask);
void init_render_pass(VkFormat surface_format, VkFormat depth_format, u8 num_draw_buffers, u8 *draw_buffers);
void destroy_render_pass();
void execute_command_buffer(bool wait);
void begin_command_buffer_recording();
void end_command_buffer_recording();
void prepare_rtts();
std::tuple<VkPrimitiveTopology, bool, u32, VkIndexType>
upload_vertex_data();
public:
bool load_program();
void init_buffers(bool skip_reading = false);
void read_buffers();
void write_buffers();
void set_viewport();
protected:
void begin() override;
void end() override;
void on_init_thread() override;
void on_exit() override;
bool do_method(u32 id, u32 arg) override;
void flip(int buffer) override;
bool on_access_violation(u32 address, bool is_writing) override;
};

View File

@ -0,0 +1,295 @@
#include "stdafx.h"
#include "VKHelpers.h"
namespace vk
{
context *g_current_vulkan_ctx = nullptr;
render_device g_current_renderer;
buffer g_null_buffer;
texture g_null_texture;
VkSampler g_null_sampler = nullptr;
VkImageView g_null_image_view = nullptr;
VKAPI_ATTR void *VKAPI_CALL mem_realloc(void *pUserData, void *pOriginal, size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
{
return realloc(pOriginal, size);
}
VKAPI_ATTR void *VKAPI_CALL mem_alloc(void *pUserData, size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
{
#ifdef _WIN32
return _aligned_malloc(size, alignment);
#else
return malloc(size);
#endif
}
VKAPI_ATTR void VKAPI_CALL mem_free(void *pUserData, void *pMemory)
{
#ifdef _WIN32
_aligned_free(pMemory);
#else
free(pMemory);
#endif
}
VkFormat get_compatible_sampler_format(u32 format, VkComponentMapping& swizzle, u8 swizzle_mask)
{
u8 remap_a = swizzle_mask & 0x3;
u8 remap_r = (swizzle_mask >> 2) & 0x3;
u8 remap_g = (swizzle_mask >> 4) & 0x3;
u8 remap_b = (swizzle_mask >> 6) & 0x3;
VkComponentSwizzle map_table[] = { VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A };
VkComponentMapping remapped;
remapped.a = map_table[remap_a];
remapped.b = map_table[remap_b];
remapped.g = map_table[remap_g];
remapped.r = map_table[remap_r];
swizzle = default_component_map();
switch (format)
{
case CELL_GCM_TEXTURE_B8:
{
swizzle = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R };
return VK_FORMAT_R8_UNORM;
}
case CELL_GCM_TEXTURE_A1R5G5B5: return VK_FORMAT_A1R5G5B5_UNORM_PACK16;
case CELL_GCM_TEXTURE_A4R4G4B4: return VK_FORMAT_B4G4R4A4_UNORM_PACK16;
case CELL_GCM_TEXTURE_R5G6B5: return VK_FORMAT_R5G6B5_UNORM_PACK16;
case CELL_GCM_TEXTURE_A8R8G8B8:
{
swizzle = remapped;
return VK_FORMAT_B8G8R8A8_UNORM;
}
case CELL_GCM_TEXTURE_COMPRESSED_DXT1: return VK_FORMAT_BC1_RGBA_UNORM_BLOCK;
case CELL_GCM_TEXTURE_COMPRESSED_DXT23: return VK_FORMAT_BC2_UNORM_BLOCK;
case CELL_GCM_TEXTURE_COMPRESSED_DXT45:
{
return VK_FORMAT_BC3_UNORM_BLOCK;
}
case CELL_GCM_TEXTURE_G8B8:
{
swizzle = { VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE };
return VK_FORMAT_R8G8_UNORM;
}
case CELL_GCM_TEXTURE_R6G5B5: return VK_FORMAT_R5G6B5_UNORM_PACK16; //Expand, discard high bit?
case CELL_GCM_TEXTURE_DEPTH24_D8: return VK_FORMAT_R32_UINT;
case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT: return VK_FORMAT_R32_SFLOAT;
case CELL_GCM_TEXTURE_DEPTH16: return VK_FORMAT_R16_UNORM;
case CELL_GCM_TEXTURE_DEPTH16_FLOAT: return VK_FORMAT_R16_SFLOAT;
case CELL_GCM_TEXTURE_X16: return VK_FORMAT_R16_UNORM;
case CELL_GCM_TEXTURE_Y16_X16: return VK_FORMAT_R16G16_UNORM;
case CELL_GCM_TEXTURE_R5G5B5A1: return VK_FORMAT_R5G5B5A1_UNORM_PACK16;
case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT: return VK_FORMAT_R16G16B16A16_SFLOAT;
case CELL_GCM_TEXTURE_W32_Z32_Y32_X32_FLOAT: return VK_FORMAT_R32G32B32A32_SFLOAT;
case CELL_GCM_TEXTURE_X32_FLOAT: return VK_FORMAT_R32_SFLOAT;
case CELL_GCM_TEXTURE_D1R5G5B5:
{
swizzle.a = VK_COMPONENT_SWIZZLE_ONE;
return VK_FORMAT_A1R5G5B5_UNORM_PACK16;
}
case CELL_GCM_TEXTURE_D8R8G8B8:
{
swizzle = remapped;
swizzle.a = VK_COMPONENT_SWIZZLE_ONE;
return VK_FORMAT_B8G8R8A8_UNORM;
}
case CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8: return VK_FORMAT_A8B8G8R8_UNORM_PACK32; //Expand
case CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8: return VK_FORMAT_R8G8B8A8_UNORM; //Expand
case CELL_GCM_TEXTURE_Y16_X16_FLOAT:
case CELL_GCM_TEXTURE_COMPRESSED_HILO8:
case CELL_GCM_TEXTURE_COMPRESSED_HILO_S8:
case ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN) & CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8:
case ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN) & CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8:
break;
}
throw EXCEPTION("Invalid or unsupported texture format (0x%x)", format);
}
VkAllocationCallbacks default_callbacks()
{
VkAllocationCallbacks callbacks;
callbacks.pfnAllocation = vk::mem_alloc;
callbacks.pfnFree = vk::mem_free;
callbacks.pfnReallocation = vk::mem_realloc;
return callbacks;
}
VkBuffer null_buffer()
{
if (g_null_buffer.size())
return g_null_buffer;
g_null_buffer.create(g_current_renderer, 32, VK_FORMAT_R32_SFLOAT, VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT);
return g_null_buffer;
}
VkSampler null_sampler()
{
if (g_null_sampler)
return g_null_sampler;
VkSamplerCreateInfo sampler_info;
memset(&sampler_info, 0, sizeof(sampler_info));
sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
sampler_info.anisotropyEnable = VK_FALSE;
sampler_info.compareEnable = VK_FALSE;
sampler_info.pNext = nullptr;
sampler_info.unnormalizedCoordinates = VK_FALSE;
sampler_info.mipLodBias = 0;
sampler_info.maxAnisotropy = 0;
sampler_info.magFilter = VK_FILTER_NEAREST;
sampler_info.minFilter = VK_FILTER_NEAREST;
sampler_info.compareOp = VK_COMPARE_OP_NEVER;
sampler_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
vkCreateSampler(g_current_renderer, &sampler_info, nullptr, &g_null_sampler);
return g_null_sampler;
}
VkImageView null_image_view()
{
if (g_null_image_view)
return g_null_image_view;
g_null_texture.create(g_current_renderer, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT, 4, 4);
g_null_image_view = g_null_texture;
return g_null_image_view;
}
VkBufferView null_buffer_view()
{
if (g_null_buffer.size())
return g_null_buffer;
g_null_buffer.create(g_current_renderer, 32, VK_FORMAT_R32_SFLOAT, VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT);
return g_null_buffer;
}
void destroy_global_resources()
{
g_null_buffer.destroy();
g_null_texture.destroy();
if (g_null_sampler)
vkDestroySampler(g_current_renderer, g_null_sampler, nullptr);
g_null_sampler = nullptr;
g_null_image_view = nullptr;
}
void set_current_thread_ctx(const vk::context &ctx)
{
g_current_vulkan_ctx = (vk::context *)&ctx;
}
context *get_current_thread_ctx()
{
return g_current_vulkan_ctx;
}
vk::render_device *get_current_renderer()
{
return &g_current_renderer;
}
void set_current_renderer(const vk::render_device &device)
{
g_current_renderer = device;
}
void change_image_layout(VkCommandBuffer cmd, VkImage image, VkImageLayout current_layout, VkImageLayout new_layout, VkImageAspectFlags aspect_flags)
{
//Prepare an image to match the new layout..
VkImageSubresourceRange range = default_image_subresource_range();
range.aspectMask = aspect_flags;
VkImageMemoryBarrier barrier;
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.pNext = nullptr;
barrier.newLayout = new_layout;
barrier.oldLayout = current_layout;
barrier.image = image;
barrier.srcAccessMask = 0;
barrier.dstAccessMask = 0;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.subresourceRange = range;
switch (new_layout)
{
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; break;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; break;
}
switch (current_layout)
{
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; break;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; break;
}
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
}
VKAPI_ATTR VkBool32 VKAPI_CALL dbgFunc(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType,
uint64_t srcObject, size_t location, int32_t msgCode,
const char *pLayerPrefix, const char *pMsg, void *pUserData)
{
if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
{
LOG_ERROR(RSX, "ERROR: [%s] Code %d : %s", pLayerPrefix, msgCode, pMsg);
}
else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
{
LOG_WARNING(RSX, "WARNING: [%s] Code %d : %s", pLayerPrefix, msgCode, pMsg);
}
else
{
return false;
}
//Let the app crash..
return false;
}
VkBool32 BreakCallback(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType,
uint64_t srcObject, size_t location, int32_t msgCode,
const char *pLayerPrefix, const char *pMsg, void *pUserData)
{
#ifdef _WIN32
DebugBreak();
#endif
return false;
}
}

1359
rpcs3/Emu/RSX/VK/VKHelpers.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,47 @@
#pragma once
#include "VKVertexProgram.h"
#include "VKFragmentProgram.h"
#include "../Common/ProgramStateCache.h"
struct VKTraits
{
using vertex_program_type = VKVertexProgram;
using fragment_program_type = VKFragmentProgram;
using pipeline_storage_type = vk::glsl::program;
using pipeline_properties = void*;
static
void recompile_fragment_program(const RSXFragmentProgram &RSXFP, fragment_program_type& fragmentProgramData, size_t ID)
{
fragmentProgramData.Decompile(RSXFP);
fragmentProgramData.Compile();
}
static
void recompile_vertex_program(const RSXVertexProgram &RSXVP, vertex_program_type& vertexProgramData, size_t ID)
{
vertexProgramData.Decompile(RSXVP);
vertexProgramData.Compile();
}
static
pipeline_storage_type build_pipeline(const vertex_program_type &vertexProgramData, const fragment_program_type &fragmentProgramData, const pipeline_properties &pipelineProperties)
{
pipeline_storage_type result(*vk::get_current_renderer());
std::vector<vk::glsl::program_input> vertex_uniforms = vertexProgramData.uniforms;
std::vector<vk::glsl::program_input> fragment_uniforms = fragmentProgramData.uniforms;
result.attachVertexProgram(vertexProgramData.handle)
.attachFragmentProgram(fragmentProgramData.handle)
.load_uniforms(vk::glsl::glsl_vertex_program, vertex_uniforms)
.load_uniforms(vk::glsl::glsl_fragment_program, fragment_uniforms)
.make();
return result;
}
};
class VKProgramBuffer : public program_state_cache<VKTraits>
{
};

View File

@ -0,0 +1,805 @@
#include "stdafx.h"
#include "VKHelpers.h"
namespace vk
{
namespace glsl
{
program::program()
{
memset(&pstate, 0, sizeof(pstate));
}
program::program(vk::render_device &renderer)
{
memset(&pstate, 0, sizeof(pstate));
init_pipeline();
device = &renderer;
}
program::program(program&& other)
{
//This object does not yet exist in a valid state. Clear the original
memset(&pstate, 0, sizeof(pstate));
pipeline_state tmp;
memcpy(&tmp, &pstate, sizeof pstate);
memcpy(&pstate, &other.pstate, sizeof pstate);
memcpy(&other.pstate, &tmp, sizeof pstate);
std::vector<program_input> tmp_uniforms = uniforms;
uniforms = other.uniforms;
other.uniforms = tmp_uniforms;
vk::descriptor_pool tmp_pool;
descriptor_pool = other.descriptor_pool;
other.descriptor_pool = tmp_pool;
vk::render_device *tmp_dev = device;
device = other.device;
other.device = tmp_dev;
bool _uniforms_changed = uniforms_changed;
uniforms_changed = other.uniforms_changed;
other.uniforms_changed = _uniforms_changed;
}
program& program::operator = (program&& other)
{
pipeline_state tmp;
memcpy(&tmp, &pstate, sizeof pstate);
memcpy(&pstate, &other.pstate, sizeof pstate);
memcpy(&other.pstate, &tmp, sizeof pstate);
std::vector<program_input> tmp_uniforms = uniforms;
uniforms = other.uniforms;
other.uniforms = tmp_uniforms;
vk::descriptor_pool tmp_pool;
descriptor_pool = other.descriptor_pool;
other.descriptor_pool = tmp_pool;
vk::render_device *tmp_dev = device;
device = other.device;
other.device = tmp_dev;
bool _uniforms_changed = uniforms_changed;
uniforms_changed = other.uniforms_changed;
other.uniforms_changed = _uniforms_changed;
return *this;
}
void program::init_pipeline()
{
pstate.dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
pstate.dynamic_state.pDynamicStates = pstate.dynamic_state_descriptors;
pstate.pipeline.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pstate.pipeline.layout = nullptr;
pstate.vi.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
pstate.ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
pstate.ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
pstate.rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
pstate.rs.polygonMode = VK_POLYGON_MODE_FILL;
pstate.rs.cullMode = VK_CULL_MODE_NONE;
pstate.rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
pstate.rs.depthClampEnable = VK_FALSE;
pstate.rs.rasterizerDiscardEnable = VK_FALSE;
pstate.rs.depthBiasEnable = VK_FALSE;
pstate.cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
pstate.cb.attachmentCount = 1;
pstate.cb.pAttachments = pstate.att_state;
for (int i = 0; i < 4; ++i)
{
pstate.att_state[i].colorWriteMask = 0xf;
pstate.att_state[i].blendEnable = VK_FALSE;
}
pstate.vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
pstate.vp.viewportCount = 1;
pstate.dynamic_state_descriptors[pstate.dynamic_state.dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT;
pstate.vp.scissorCount = 1;
pstate.dynamic_state_descriptors[pstate.dynamic_state.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR;
pstate.dynamic_state_descriptors[pstate.dynamic_state.dynamicStateCount++] = VK_DYNAMIC_STATE_LINE_WIDTH;
pstate.ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
pstate.ds.depthTestEnable = VK_FALSE;
pstate.ds.depthWriteEnable = VK_TRUE;
pstate.ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
pstate.ds.depthBoundsTestEnable = VK_FALSE;
pstate.ds.back.failOp = VK_STENCIL_OP_KEEP;
pstate.ds.back.passOp = VK_STENCIL_OP_KEEP;
pstate.ds.back.compareOp = VK_COMPARE_OP_ALWAYS;
pstate.ds.stencilTestEnable = VK_FALSE;
pstate.ds.front = pstate.ds.back;
pstate.ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
pstate.ms.pSampleMask = NULL;
pstate.ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
pstate.fs = nullptr;
pstate.vs = nullptr;
pstate.dirty = true;
pstate.pipeline.stageCount = 2;
pstate.shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
pstate.shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
pstate.shader_stages[0].module = nullptr;
pstate.shader_stages[0].pName = "main";
pstate.shader_stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
pstate.shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
pstate.shader_stages[1].module = nullptr;
pstate.shader_stages[1].pName = "main";
pstate.pipeline_cache_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
}
program::~program()
{
LOG_ERROR(RSX, "Program destructor invoked!");
destroy();
}
program& program::attach_device(vk::render_device &dev)
{
if (!device)
init_pipeline();
device = &dev;
return *this;
}
program& program::attachFragmentProgram(VkShaderModule prog)
{
pstate.fs = prog;
return *this;
}
program& program::attachVertexProgram(VkShaderModule prog)
{
pstate.vs = prog;
return *this;
}
void program::make()
{
if (pstate.fs == nullptr || pstate.vs == nullptr)
throw EXCEPTION("Missing shader stage!");
pstate.shader_stages[0].module = pstate.vs;
pstate.shader_stages[1].module = pstate.fs;
CHECK_RESULT(vkCreatePipelineCache((*device), &pstate.pipeline_cache_desc, nullptr, &pstate.pipeline_cache));
}
void program::set_depth_compare_op(VkCompareOp op)
{
if (pstate.ds.depthCompareOp != op)
{
pstate.ds.depthCompareOp = op;
pstate.dirty = true;
}
}
void program::set_depth_write_mask(VkBool32 write_enable)
{
if (pstate.ds.depthWriteEnable != write_enable)
{
pstate.ds.depthWriteEnable = write_enable;
pstate.dirty = true;
}
}
void program::set_depth_test_enable(VkBool32 state)
{
if (pstate.ds.depthTestEnable != state)
{
pstate.ds.depthTestEnable = state;
pstate.dirty = true;
}
}
void program::set_primitive_topology(VkPrimitiveTopology topology)
{
if (pstate.ia.topology != topology)
{
pstate.ia.topology = topology;
pstate.dirty = true;
}
}
void program::set_color_mask(int num_targets, u8* targets, VkColorComponentFlags* flags)
{
if (num_targets)
{
for (u8 idx = 0; idx < num_targets; ++idx)
{
u8 &id = targets[idx];
if (pstate.att_state[id].colorWriteMask != flags[idx])
{
pstate.att_state[id].colorWriteMask = flags[idx];
pstate.dirty = true;
}
}
}
}
void program::set_blend_state(int num_targets, u8* targets, VkBool32* enable)
{
if (num_targets)
{
for (u8 idx = 0; idx < num_targets; ++idx)
{
u8 &id = targets[idx];
if (pstate.att_state[id].blendEnable != enable[idx])
{
pstate.att_state[id].blendEnable = enable[idx];
pstate.dirty = true;
}
}
}
}
void program::set_blend_state(int num_targets, u8 *targets, VkBool32 enable)
{
for (u8 idx = 0; idx < num_targets; ++idx)
{
u8 &id = targets[idx];
if (pstate.att_state[id].blendEnable != enable)
{
pstate.att_state[id].blendEnable = enable;
pstate.dirty = true;
}
}
}
void program::set_blend_func(int num_targets, u8* targets, VkBlendFactor* src_color, VkBlendFactor* dst_color, VkBlendFactor* src_alpha, VkBlendFactor* dst_alpha)
{
if (num_targets)
{
for (u8 idx = 0; idx < num_targets; ++idx)
{
u8 &id = targets[idx];
if (pstate.att_state[id].srcColorBlendFactor != src_color[idx])
{
pstate.att_state[id].srcColorBlendFactor = src_color[idx];
pstate.dirty = true;
}
if (pstate.att_state[id].dstColorBlendFactor != dst_color[idx])
{
pstate.att_state[id].dstColorBlendFactor = dst_color[idx];
pstate.dirty = true;
}
if (pstate.att_state[id].srcAlphaBlendFactor != src_alpha[idx])
{
pstate.att_state[id].srcAlphaBlendFactor = src_alpha[idx];
pstate.dirty = true;
}
if (pstate.att_state[id].dstAlphaBlendFactor != dst_alpha[idx])
{
pstate.att_state[id].dstAlphaBlendFactor = dst_alpha[idx];
pstate.dirty = true;
}
}
}
}
void program::set_blend_func(int num_targets, u8* targets, VkBlendFactor src_color, VkBlendFactor dst_color, VkBlendFactor src_alpha, VkBlendFactor dst_alpha)
{
if (num_targets)
{
for (u8 idx = 0; idx < num_targets; ++idx)
{
u8 &id = targets[idx];
if (pstate.att_state[id].srcColorBlendFactor != src_color)
{
pstate.att_state[id].srcColorBlendFactor = src_color;
pstate.dirty = true;
}
if (pstate.att_state[id].dstColorBlendFactor != dst_color)
{
pstate.att_state[id].dstColorBlendFactor = dst_color;
pstate.dirty = true;
}
if (pstate.att_state[id].srcAlphaBlendFactor != src_alpha)
{
pstate.att_state[id].srcAlphaBlendFactor = src_alpha;
pstate.dirty = true;
}
if (pstate.att_state[id].dstAlphaBlendFactor != dst_alpha)
{
pstate.att_state[id].dstAlphaBlendFactor = dst_alpha;
pstate.dirty = true;
}
}
}
}
void program::set_blend_op(int num_targets, u8* targets, VkBlendOp* color_ops, VkBlendOp* alpha_ops)
{
if (num_targets)
{
for (u8 idx = 0; idx < num_targets; ++idx)
{
u8 &id = targets[idx];
if (pstate.att_state[id].colorBlendOp != color_ops[idx])
{
pstate.att_state[id].colorBlendOp = color_ops[idx];
pstate.dirty = true;
}
if (pstate.att_state[id].alphaBlendOp != alpha_ops[idx])
{
pstate.att_state[id].alphaBlendOp = alpha_ops[idx];
pstate.dirty = true;
}
}
}
}
void program::set_blend_op(int num_targets, u8* targets, VkBlendOp color_op, VkBlendOp alpha_op)
{
if (num_targets)
{
for (u8 idx = 0; idx < num_targets; ++idx)
{
u8 &id = targets[idx];
if (pstate.att_state[id].colorBlendOp != color_op)
{
pstate.att_state[id].colorBlendOp = color_op;
pstate.dirty = true;
}
if (pstate.att_state[id].alphaBlendOp != alpha_op)
{
pstate.att_state[id].alphaBlendOp = alpha_op;
pstate.dirty = true;
}
}
}
}
void program::set_primitive_restart(VkBool32 state)
{
if (pstate.ia.primitiveRestartEnable != state)
{
pstate.ia.primitiveRestartEnable = state;
pstate.dirty = true;
}
}
void program::init_descriptor_layout()
{
if (pstate.descriptor_layouts[0] != nullptr)
throw EXCEPTION("Existing descriptors found!");
if (descriptor_pool.valid())
descriptor_pool.destroy();
std::vector<VkDescriptorSetLayoutBinding> layout_bindings[2];
std::vector<VkDescriptorPoolSize> sizes;
program_input_type types[] = { input_type_uniform_buffer, input_type_texel_buffer, input_type_texture };
program_domain stages[] = { glsl_vertex_program, glsl_fragment_program };
VkDescriptorType vk_ids[] = { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER };
VkShaderStageFlags vk_stages[] = { VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_STAGE_FRAGMENT_BIT };
for (auto &input : uniforms)
{
VkDescriptorSetLayoutBinding binding;
binding.binding = input.location;
binding.descriptorCount = 1;
binding.descriptorType = vk_ids[(u32)input.type];
binding.pImmutableSamplers = nullptr;
binding.stageFlags = vk_stages[(u32)input.domain];
layout_bindings[(u32)input.domain].push_back(binding);
}
for (int i = 0; i < 3; ++i)
{
u32 count = 0;
for (auto &input : uniforms)
{
if (input.type == types[i])
count++;
}
if (!count) continue;
VkDescriptorPoolSize size;
size.descriptorCount = count;
size.type = vk_ids[i];
sizes.push_back(size);
}
descriptor_pool.create((*device), sizes.data(), sizes.size());
VkDescriptorSetLayoutCreateInfo infos;
infos.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
infos.pNext = nullptr;
infos.flags = 0;
infos.pBindings = layout_bindings[0].data();
infos.bindingCount = layout_bindings[0].size();
CHECK_RESULT(vkCreateDescriptorSetLayout((*device), &infos, nullptr, &pstate.descriptor_layouts[0]));
infos.pBindings = layout_bindings[1].data();
infos.bindingCount = layout_bindings[1].size();
CHECK_RESULT(vkCreateDescriptorSetLayout((*device), &infos, nullptr, &pstate.descriptor_layouts[1]));
VkPipelineLayoutCreateInfo layout_info;
layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
layout_info.pNext = nullptr;
layout_info.setLayoutCount = 2;
layout_info.pSetLayouts = pstate.descriptor_layouts;
layout_info.flags = 0;
layout_info.pPushConstantRanges = nullptr;
layout_info.pushConstantRangeCount = 0;
CHECK_RESULT(vkCreatePipelineLayout((*device), &layout_info, nullptr, &pstate.pipeline_layout));
VkDescriptorSetAllocateInfo alloc_info;
alloc_info.descriptorPool = descriptor_pool;
alloc_info.descriptorSetCount = 2;
alloc_info.pNext = nullptr;
alloc_info.pSetLayouts = pstate.descriptor_layouts;
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
CHECK_RESULT(vkAllocateDescriptorSets((*device), &alloc_info, pstate.descriptor_sets));
}
void program::update_descriptors()
{
if (!pstate.descriptor_layouts[0])
init_descriptor_layout();
std::vector<VkWriteDescriptorSet> descriptor_writers;
std::vector<VkDescriptorImageInfo> images(16);
std::vector<VkDescriptorBufferInfo> buffers(16);
std::vector<VkDescriptorBufferInfo> texel_buffers(16);
std::vector<VkBufferView> texel_buffer_views(16);
VkWriteDescriptorSet write;
int image_index = 0;
int buffer_index = 0;
int texel_buffer_index = 0;
for (auto &input : uniforms)
{
switch (input.type)
{
case input_type_texture:
{
auto &image = images[image_index++];
image.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image.sampler = null_sampler();
image.imageView = null_image_view();
if (input.as_sampler.sampler && input.as_sampler.image_view)
{
image.imageView = input.as_sampler.image_view;
image.sampler = input.as_sampler.sampler;
image.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
else
LOG_ERROR(RSX, "Texture object was not bound: %s", input.name);
memset(&write, 0, sizeof(write));
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write.pImageInfo = &image;
write.descriptorCount = 1;
break;
}
case input_type_uniform_buffer:
{
auto &buffer = buffers[buffer_index++];
buffer.buffer = null_buffer();
buffer.offset = 0;
buffer.range = 0;
if (input.as_buffer.buffer)
{
buffer.buffer = input.as_buffer.buffer;
buffer.range = input.as_buffer.size;
}
else
LOG_ERROR(RSX, "UBO was not bound: %s", input.name);
memset(&write, 0, sizeof(write));
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
write.pBufferInfo = &buffer;
write.descriptorCount = 1;
break;
}
case input_type_texel_buffer:
{
auto &buffer_view = texel_buffer_views[texel_buffer_index];
buffer_view = null_buffer_view();
auto &buffer = texel_buffers[texel_buffer_index++];
buffer.buffer = null_buffer();
buffer.offset = 0;
buffer.range = 0;
if (input.as_buffer.buffer && input.as_buffer.buffer_view)
{
buffer_view = input.as_buffer.buffer_view;
buffer.buffer = input.as_buffer.buffer;
buffer.range = input.as_buffer.size;
}
else
LOG_ERROR(RSX, "Texel buffer was not bound: %s", input.name);
memset(&write, 0, sizeof(write));
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
write.pTexelBufferView = &buffer_view;
write.pBufferInfo = &buffer;
write.descriptorCount = 1;
break;
}
default:
throw EXCEPTION("Unhandled input type!");
}
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.dstSet = pstate.descriptor_sets[input.domain];
write.pNext = nullptr;
write.dstBinding = input.location;
descriptor_writers.push_back(write);
}
if (!descriptor_writers.size()) return;
if (descriptor_writers.size() != uniforms.size())
throw EXCEPTION("Undefined uniform detected");
vkUpdateDescriptorSets((*device), descriptor_writers.size(), descriptor_writers.data(), 0, nullptr);
}
void program::destroy_descriptors()
{
if (pstate.descriptor_sets[0])
vkFreeDescriptorSets((*device), descriptor_pool, 2, pstate.descriptor_sets);
if (pstate.pipeline_layout)
vkDestroyPipelineLayout((*device), pstate.pipeline_layout, nullptr);
if (pstate.descriptor_layouts[0])
vkDestroyDescriptorSetLayout((*device), pstate.descriptor_layouts[0], nullptr);
if (pstate.descriptor_layouts[1])
vkDestroyDescriptorSetLayout((*device), pstate.descriptor_layouts[1], nullptr);
descriptor_pool.destroy();
}
void program::set_draw_buffer_count(u8 draw_buffers)
{
if (pstate.num_targets != draw_buffers)
{
pstate.num_targets = draw_buffers;
pstate.dirty = true;
}
}
program& program::load_uniforms(program_domain domain, std::vector<program_input>& inputs)
{
std::vector<program_input> store = uniforms;
uniforms.resize(0);
for (auto &item : store)
{
if (item.domain != domain)
uniforms.push_back(item);
}
for (auto &item : inputs)
uniforms.push_back(item);
return *this;
}
void program::use(vk::command_buffer& commands, VkRenderPass pass, u32 subpass)
{
if (/*uniforms_changed*/true)
{
update_descriptors();
uniforms_changed = false;
}
if (pstate.dirty)
{
if (pstate.pipeline_handle)
vkDestroyPipeline((*device), pstate.pipeline_handle, nullptr);
pstate.dynamic_state.pDynamicStates = pstate.dynamic_state_descriptors;
pstate.cb.pAttachments = pstate.att_state;
pstate.cb.attachmentCount = pstate.num_targets;
//Reconfigure this..
pstate.pipeline.pVertexInputState = &pstate.vi;
pstate.pipeline.pInputAssemblyState = &pstate.ia;
pstate.pipeline.pRasterizationState = &pstate.rs;
pstate.pipeline.pColorBlendState = &pstate.cb;
pstate.pipeline.pMultisampleState = &pstate.ms;
pstate.pipeline.pViewportState = &pstate.vp;
pstate.pipeline.pDepthStencilState = &pstate.ds;
pstate.pipeline.pStages = pstate.shader_stages;
pstate.pipeline.pDynamicState = &pstate.dynamic_state;
pstate.pipeline.layout = pstate.pipeline_layout;
pstate.pipeline.basePipelineIndex = -1;
pstate.pipeline.basePipelineHandle = VK_NULL_HANDLE;
pstate.pipeline.renderPass = pass;
CHECK_RESULT(vkCreateGraphicsPipelines((*device), nullptr, 1, &pstate.pipeline, NULL, &pstate.pipeline_handle));
pstate.dirty = false;
}
vkCmdBindPipeline(commands, VK_PIPELINE_BIND_POINT_GRAPHICS, pstate.pipeline_handle);
vkCmdBindDescriptorSets(commands, VK_PIPELINE_BIND_POINT_GRAPHICS, pstate.pipeline_layout, 0, 2, pstate.descriptor_sets, 0, nullptr);
}
bool program::has_uniform(program_domain domain, std::string uniform_name)
{
for (auto &uniform : uniforms)
{
if (uniform.name == uniform_name &&
uniform.domain == domain)
return true;
}
return false;
}
bool program::bind_uniform(program_domain domain, std::string uniform_name)
{
for (auto &uniform : uniforms)
{
if (uniform.name == uniform_name &&
uniform.domain == domain)
{
uniform.as_buffer.buffer = nullptr;
uniform.as_buffer.buffer_view = nullptr;
uniform.as_sampler.image_view = nullptr;
uniform.as_sampler.sampler = nullptr;
uniforms_changed = true;
return true;
}
}
return false;
}
bool program::bind_uniform(program_domain domain, std::string uniform_name, vk::texture &_texture)
{
for (auto &uniform : uniforms)
{
if (uniform.name == uniform_name &&
uniform.domain == domain)
{
VkImageView view = _texture;
VkSampler sampler = _texture;
if (uniform.as_sampler.image_view != view ||
uniform.as_sampler.sampler != sampler)
{
uniform.as_sampler.image_view = view;
uniform.as_sampler.sampler = sampler;
uniforms_changed = true;
}
uniform.type = input_type_texture;
return true;
}
}
return false;
}
bool program::bind_uniform(program_domain domain, std::string uniform_name, vk::buffer &_buffer)
{
for (auto &uniform : uniforms)
{
if (uniform.name == uniform_name &&
uniform.domain == domain)
{
VkBuffer buf = _buffer;
u64 size = _buffer.size();
if (uniform.as_buffer.buffer != buf ||
uniform.as_buffer.size != size)
{
uniform.as_buffer.size = size;
uniform.as_buffer.buffer = buf;
uniform.as_buffer.buffer_view = nullptr; //UBOs cannot be viewed!
uniforms_changed = true;
}
uniform.type = input_type_uniform_buffer;
return true;
}
}
throw EXCEPTION("Failed to bind program uniform %s", uniform_name);
return false;
}
bool program::bind_uniform(program_domain domain, std::string uniform_name, vk::buffer &_buffer, bool is_texel_store)
{
if (!is_texel_store)
{
return bind_uniform(domain, uniform_name, _buffer);
}
for (auto &uniform : uniforms)
{
if (uniform.name == uniform_name &&
uniform.domain == domain)
{
VkBuffer buf = _buffer;
VkBufferView view = _buffer;
u64 size = _buffer.size();
if (uniform.as_buffer.buffer != buf ||
uniform.as_buffer.buffer_view != view ||
uniform.as_buffer.size != size)
{
uniform.as_buffer.size = size;
uniform.as_buffer.buffer = buf;
uniform.as_buffer.buffer_view = view;
if (!view)
throw EXCEPTION("Invalid buffer passed as texel storage");
uniforms_changed = true;
}
uniform.type = input_type_texel_buffer;
return true;
}
}
return false;
}
void program::destroy()
{
if (device)
{
destroy_descriptors();
uniforms.resize(0);
if (pstate.pipeline_handle)
vkDestroyPipeline((*device), pstate.pipeline_handle, nullptr);
if (pstate.pipeline_cache)
vkDestroyPipelineCache((*device), pstate.pipeline_cache, nullptr);
}
memset(&pstate, 0, sizeof pstate);
device = nullptr;
}
}
}

View File

@ -0,0 +1,148 @@
#pragma once
#include "stdafx.h"
#include "VKHelpers.h"
#include "../GCM.h"
#include "../Common/surface_store.h"
namespace rsx
{
struct vk_render_target_traits
{
using surface_storage_type = vk::texture ;
using surface_type = vk::texture*;
using command_list_type = vk::command_buffer*;
using download_buffer_object = void*;
static vk::texture create_new_surface(u32 address, surface_color_format format, size_t width, size_t height, vk::render_device &device, vk::command_buffer *cmd)
{
VkFormat requested_format = vk::get_compatible_surface_format(format);
vk::texture rtt;
rtt.create(device, requested_format, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_SAMPLED_BIT, width, height, 1, true);
rtt.change_layout(*cmd, VK_IMAGE_LAYOUT_GENERAL);
//Clear new surface
VkClearColorValue clear_color;
VkImageSubresourceRange range = vk::default_image_subresource_range();
clear_color.float32[0] = 0.f;
clear_color.float32[1] = 0.f;
clear_color.float32[2] = 0.f;
clear_color.float32[3] = 0.f;
vkCmdClearColorImage(*cmd, rtt, rtt.get_layout(), &clear_color, 1, &range);
rtt.change_layout(*cmd, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
return rtt;
}
static vk::texture create_new_surface(u32 address, surface_depth_format format, size_t width, size_t height, vk::render_device &device, vk::command_buffer *cmd)
{
VkFormat requested_format = vk::get_compatible_depth_surface_format(format);
vk::texture rtt;
rtt.create(device, requested_format, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT|VK_IMAGE_USAGE_SAMPLED_BIT, width, height, 1, true);
rtt.change_layout(*cmd, VK_IMAGE_LAYOUT_GENERAL);
//Clear new surface..
VkClearDepthStencilValue clear_depth;
VkImageSubresourceRange range;
range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
range.baseArrayLayer = 0;
range.baseMipLevel = 0;
range.layerCount = 1;
range.levelCount = 1;
if (requested_format != VK_FORMAT_D16_UNORM)
range.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
clear_depth.depth = 1.f;
clear_depth.stencil = 0;
vkCmdClearDepthStencilImage(*cmd, rtt, rtt.get_layout(), &clear_depth, 1, &range);
rtt.change_layout(*cmd, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
return rtt;
}
static void prepare_rtt_for_drawing(vk::command_buffer* pcmd, vk::texture *surface)
{
surface->change_layout(*pcmd, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
}
static void prepare_rtt_for_sampling(vk::command_buffer* pcmd, vk::texture *surface)
{
surface->change_layout(*pcmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
static void prepare_ds_for_drawing(vk::command_buffer* pcmd, vk::texture *surface)
{
surface->change_layout(*pcmd, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
}
static void prepare_ds_for_sampling(vk::command_buffer* pcmd, vk::texture *surface)
{
surface->change_layout(*pcmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
static bool rtt_has_format_width_height(const vk::texture &rtt, surface_color_format format, size_t width, size_t height)
{
VkFormat fmt = vk::get_compatible_surface_format(format);
vk::texture &tex = const_cast<vk::texture&>(rtt);
if (tex.get_format() == fmt &&
tex.width() == width &&
tex.height() == height)
return true;
return false;
}
static bool ds_has_format_width_height(const vk::texture &ds, surface_depth_format format, size_t width, size_t height)
{
VkFormat fmt = vk::get_compatible_depth_surface_format(format);
vk::texture &tex = const_cast<vk::texture&>(ds);
if (tex.get_format() == fmt &&
tex.width() == width &&
tex.height() == height)
return true;
return false;
}
static download_buffer_object issue_download_command(surface_type, surface_color_format color_format, size_t width, size_t height, ...)
{
return nullptr;
}
static download_buffer_object issue_depth_download_command(surface_type, surface_depth_format depth_format, size_t width, size_t height, ...)
{
return nullptr;
}
static download_buffer_object issue_stencil_download_command(surface_type, surface_depth_format depth_format, size_t width, size_t height, ...)
{
return nullptr;
}
gsl::span<const gsl::byte> map_downloaded_buffer(download_buffer_object, ...)
{
return{ (gsl::byte*)nullptr, 0 };
}
static void unmap_downloaded_buffer(download_buffer_object, ...)
{
}
static vk::texture *get(const vk::texture &tex)
{
return const_cast<vk::texture*>(&tex);
}
};
struct vk_render_targets : public rsx::surface_store<vk_render_target_traits>
{
};
}

View File

@ -0,0 +1,586 @@
#include "stdafx.h"
#include "VKHelpers.h"
#include "../GCM.h"
#include "../RSXThread.h"
#include "../RSXTexture.h"
#include "../rsx_utils.h"
#include "../Common/TextureUtils.h"
namespace vk
{
VkComponentMapping default_component_map()
{
VkComponentMapping result;
result.a = VK_COMPONENT_SWIZZLE_A;
result.r = VK_COMPONENT_SWIZZLE_R;
result.g = VK_COMPONENT_SWIZZLE_G;
result.b = VK_COMPONENT_SWIZZLE_B;
return result;
}
VkImageSubresource default_image_subresource()
{
VkImageSubresource subres;
subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subres.mipLevel = 0;
subres.arrayLayer = 0;
return subres;
}
VkImageSubresourceRange default_image_subresource_range()
{
VkImageSubresourceRange subres;
subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subres.baseArrayLayer = 0;
subres.baseMipLevel = 0;
subres.layerCount = 1;
subres.levelCount = 1;
return subres;
}
void copy_image(VkCommandBuffer cmd, VkImage &src, VkImage &dst, VkImageLayout srcLayout, VkImageLayout dstLayout, u32 width, u32 height, u32 mipmaps, VkImageAspectFlagBits aspect)
{
VkImageSubresourceLayers a_src, a_dst;
a_src.aspectMask = aspect;
a_src.baseArrayLayer = 0;
a_src.layerCount = 1;
a_src.mipLevel = 0;
a_dst = a_src;
VkImageCopy rgn;
rgn.extent.depth = 1;
rgn.extent.width = width;
rgn.extent.height = height;
rgn.dstOffset = { 0, 0, 0 };
rgn.srcOffset = { 0, 0, 0 };
rgn.srcSubresource = a_src;
rgn.dstSubresource = a_dst;
if (srcLayout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
change_image_layout(cmd, src, srcLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, aspect);
if (dstLayout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
change_image_layout(cmd, dst, dstLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, aspect);
for (u32 mip_level = 0; mip_level < mipmaps; ++mip_level)
{
vkCmdCopyImage(cmd, src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &rgn);
rgn.srcSubresource.mipLevel++;
rgn.dstSubresource.mipLevel++;
}
if (srcLayout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
change_image_layout(cmd, src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, srcLayout, aspect);
if (dstLayout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
change_image_layout(cmd, dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dstLayout, aspect);
}
void copy_scaled_image(VkCommandBuffer cmd, VkImage & src, VkImage & dst, VkImageLayout srcLayout, VkImageLayout dstLayout, u32 src_width, u32 src_height, u32 dst_width, u32 dst_height, u32 mipmaps, VkImageAspectFlagBits aspect)
{
VkImageSubresourceLayers a_src, a_dst;
a_src.aspectMask = aspect;
a_src.baseArrayLayer = 0;
a_src.layerCount = 1;
a_src.mipLevel = 0;
a_dst = a_src;
VkImageBlit rgn;
rgn.srcOffsets[0] = { 0, 0, 0 };
rgn.srcOffsets[1] = { (int32_t)src_width, (int32_t)src_height, 1 };
rgn.dstOffsets[0] = { 0, 0, 0 };
rgn.dstOffsets[1] = { (int32_t)dst_width, (int32_t)dst_height, 1 };
rgn.dstSubresource = a_dst;
rgn.srcSubresource = a_src;
if (srcLayout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
change_image_layout(cmd, src, srcLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, aspect);
if (dstLayout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
change_image_layout(cmd, dst, dstLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, aspect);
for (u32 mip_level = 0; mip_level < mipmaps; ++mip_level)
{
vkCmdBlitImage(cmd, src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &rgn, VK_FILTER_LINEAR);
rgn.srcSubresource.mipLevel++;
rgn.dstSubresource.mipLevel++;
}
if (srcLayout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
change_image_layout(cmd, src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, srcLayout, aspect);
if (dstLayout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
change_image_layout(cmd, dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dstLayout, aspect);
}
void copy_texture(VkCommandBuffer cmd, texture &src, texture &dst, VkImageLayout srcLayout, VkImageLayout dstLayout, u32 width, u32 height, u32 mipmaps, VkImageAspectFlagBits aspect)
{
VkImage isrc = (VkImage)src;
VkImage idst = (VkImage)dst;
copy_image(cmd, isrc, idst, srcLayout, dstLayout, width, height, mipmaps, aspect);
}
texture::texture(vk::swap_chain_image &img)
{
m_image_contents = img;
m_view = img;
m_sampler = nullptr;
//We did not create this object, do not allow internal modification!
owner = nullptr;
}
void texture::create(vk::render_device &device, VkFormat format, VkImageType image_type, VkImageViewType view_type, VkImageCreateFlags image_flags, VkImageUsageFlags usage, VkImageTiling tiling, u32 width, u32 height, u32 mipmaps, bool gpu_only, VkComponentMapping swizzle)
{
owner = &device;
//First create the image
VkImageCreateInfo image_info;
memset(&image_info, 0, sizeof(image_info));
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_info.pNext = nullptr;
image_info.imageType = image_type;
image_info.format = format;
image_info.extent = { width, height, 1 };
image_info.mipLevels = mipmaps;
image_info.arrayLayers = (image_flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT)? 6: 1;
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_info.tiling = tiling;
image_info.usage = usage;
image_info.flags = image_flags;
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
CHECK_RESULT(vkCreateImage(device, &image_info, nullptr, &m_image_contents));
vkGetImageMemoryRequirements(device, m_image_contents, &m_memory_layout);
vram_allocation.allocate_from_pool(device, m_memory_layout.size, !gpu_only, m_memory_layout.memoryTypeBits);
CHECK_RESULT(vkBindImageMemory(device, m_image_contents, vram_allocation, 0));
VkImageViewCreateInfo view_info;
view_info.format = format;
view_info.image = m_image_contents;
view_info.pNext = nullptr;
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
view_info.viewType = view_type;
view_info.components = swizzle;
view_info.subresourceRange = default_image_subresource_range();
view_info.flags = 0;
if (usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
{
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
m_image_aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
}
CHECK_RESULT(vkCreateImageView(device, &view_info, nullptr, &m_view));
m_width = width;
m_height = height;
m_mipmaps = mipmaps;
m_internal_format = format;
m_flags = usage;
m_view_type = view_type;
m_usage = usage;
m_tiling = tiling;
if (usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT ||
usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
{
VkSamplerAddressMode clamp_s = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
VkSamplerAddressMode clamp_t = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
VkSamplerAddressMode clamp_r = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
VkSamplerCreateInfo sampler_info;
sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
sampler_info.addressModeU = clamp_s;
sampler_info.addressModeV = clamp_t;
sampler_info.addressModeW = clamp_r;
sampler_info.anisotropyEnable = VK_FALSE;
sampler_info.compareEnable = VK_FALSE;
sampler_info.pNext = nullptr;
sampler_info.unnormalizedCoordinates = VK_FALSE;
sampler_info.mipLodBias = 0;
sampler_info.maxAnisotropy = 0;
sampler_info.flags = 0;
sampler_info.magFilter = VK_FILTER_LINEAR;
sampler_info.minFilter = VK_FILTER_LINEAR;
sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
sampler_info.compareOp = VK_COMPARE_OP_NEVER;
sampler_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
CHECK_RESULT(vkCreateSampler((*owner), &sampler_info, nullptr, &m_sampler));
}
ready = true;
}
void texture::create(vk::render_device &device, VkFormat format, VkImageUsageFlags usage, VkImageTiling tiling, u32 width, u32 height, u32 mipmaps, bool gpu_only, VkComponentMapping swizzle)
{
create(device, format, VK_IMAGE_TYPE_2D, VK_IMAGE_VIEW_TYPE_2D, 0, usage, tiling, width, height, mipmaps, gpu_only, swizzle);
}
void texture::create(vk::render_device &device, VkFormat format, VkImageUsageFlags usage, u32 width, u32 height, u32 mipmaps, bool gpu_only, VkComponentMapping swizzle)
{
VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL;
/* The spec mandates checking against all usage bits for support in either linear or optimal tiling modes.
* Ideally, no assumptions should be made, but for simplification, we'll assume optimal mode suppoorts everything
*/
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(device.gpu(), format, &props);
bool linear_is_supported = true;
if (!!(usage & VK_IMAGE_USAGE_SAMPLED_BIT))
{
if (!(props.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT))
linear_is_supported = false;
}
if (linear_is_supported && !!(usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT))
{
if (!(props.linearTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT))
linear_is_supported = false;
}
if (linear_is_supported && !!(usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT))
{
if (!(props.linearTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT))
linear_is_supported = false;
}
if (linear_is_supported && !!(usage & VK_IMAGE_USAGE_STORAGE_BIT))
{
if (!(props.linearTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT))
linear_is_supported = false;
}
if (linear_is_supported)
tiling = VK_IMAGE_TILING_LINEAR;
else
usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
create(device, format, usage, tiling, width, height, mipmaps, gpu_only, swizzle);
}
void texture::create(vk::render_device &device, VkFormat format, VkImageUsageFlags usage, u32 width, u32 height, u32 mipmaps, bool gpu_only)
{
create(device, format, usage, width, height, mipmaps, gpu_only, default_component_map());
}
void texture::create(vk::render_device &device, VkFormat format, VkImageUsageFlags usage, u32 width, u32 height)
{
create(device, format, usage, width, height, 1, false);
}
VkSamplerAddressMode texture::vk_wrap_mode(u32 gcm_wrap)
{
switch (gcm_wrap)
{
case CELL_GCM_TEXTURE_WRAP: return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case CELL_GCM_TEXTURE_MIRROR: return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
case CELL_GCM_TEXTURE_CLAMP_TO_EDGE: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case CELL_GCM_TEXTURE_BORDER: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
case CELL_GCM_TEXTURE_CLAMP: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case CELL_GCM_TEXTURE_MIRROR_ONCE_CLAMP_TO_EDGE: return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
case CELL_GCM_TEXTURE_MIRROR_ONCE_BORDER: return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
case CELL_GCM_TEXTURE_MIRROR_ONCE_CLAMP: return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
default:
throw EXCEPTION("unhandled texture clamp mode 0x%X", gcm_wrap);
}
}
float texture::max_aniso(u32 gcm_aniso)
{
switch (gcm_aniso)
{
case CELL_GCM_TEXTURE_MAX_ANISO_1: return 1.0f;
case CELL_GCM_TEXTURE_MAX_ANISO_2: return 2.0f;
case CELL_GCM_TEXTURE_MAX_ANISO_4: return 4.0f;
case CELL_GCM_TEXTURE_MAX_ANISO_6: return 6.0f;
case CELL_GCM_TEXTURE_MAX_ANISO_8: return 8.0f;
case CELL_GCM_TEXTURE_MAX_ANISO_10: return 10.0f;
case CELL_GCM_TEXTURE_MAX_ANISO_12: return 12.0f;
case CELL_GCM_TEXTURE_MAX_ANISO_16: return 16.0f;
}
LOG_ERROR(RSX, "Texture anisotropy error: bad max aniso (%d).", gcm_aniso);
return 1.0f;
}
void texture::sampler_setup(rsx::texture &tex, VkImageViewType type, VkComponentMapping swizzle)
{
VkSamplerAddressMode clamp_s = vk_wrap_mode(tex.wrap_s());
VkSamplerAddressMode clamp_t = vk_wrap_mode(tex.wrap_t());
VkSamplerAddressMode clamp_r = vk_wrap_mode(tex.wrap_r());
VkSamplerCreateInfo sampler_info;
sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
sampler_info.addressModeU = clamp_s;
sampler_info.addressModeV = clamp_t;
sampler_info.addressModeW = clamp_r;
sampler_info.anisotropyEnable = VK_TRUE;
sampler_info.compareEnable = VK_FALSE;
sampler_info.pNext = nullptr;
sampler_info.unnormalizedCoordinates = !!(tex.format() & CELL_GCM_TEXTURE_UN);
sampler_info.mipLodBias = tex.bias();
sampler_info.maxAnisotropy = max_aniso(tex.max_aniso());
sampler_info.flags = 0;
sampler_info.maxLod = tex.max_lod();
sampler_info.minLod = tex.min_lod();
sampler_info.magFilter = VK_FILTER_LINEAR;
sampler_info.minFilter = VK_FILTER_LINEAR;
sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
sampler_info.compareOp = VK_COMPARE_OP_NEVER;
sampler_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
CHECK_RESULT(vkCreateSampler((*owner), &sampler_info, nullptr, &m_sampler));
}
void texture::init(rsx::texture& tex, vk::command_buffer &cmd, bool ignore_checks)
{
VkImageViewType best_type = VK_IMAGE_VIEW_TYPE_2D;
if (tex.cubemap() && m_view_type != VK_IMAGE_VIEW_TYPE_CUBE)
{
vk::render_device &dev = (*owner);
VkFormat format = m_internal_format;
VkImageUsageFlags usage = m_usage;
VkImageTiling tiling = m_tiling;
destroy();
create(dev, format, VK_IMAGE_TYPE_2D, VK_IMAGE_VIEW_TYPE_CUBE, VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, usage, tiling, tex.width(), tex.height(), tex.mipmap(), false, default_component_map());
}
if (!tex.cubemap() && tex.depth() > 1 && m_view_type != VK_IMAGE_VIEW_TYPE_3D)
{
best_type = VK_IMAGE_VIEW_TYPE_3D;
vk::render_device &dev = (*owner);
VkFormat format = m_internal_format;
VkImageUsageFlags usage = m_usage;
VkImageTiling tiling = m_tiling;
destroy();
create(dev, format, VK_IMAGE_TYPE_3D, VK_IMAGE_VIEW_TYPE_3D, 0, usage, tiling, tex.width(), tex.height(), tex.mipmap(), false, default_component_map());
}
if (!m_sampler)
sampler_setup(tex, best_type, default_component_map());
VkImageSubresource subres;
subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subres.mipLevel = 0;
subres.arrayLayer = 0;
u8 *data;
VkFormatProperties props;
vk::physical_device dev = owner->gpu();
vkGetPhysicalDeviceFormatProperties(dev, m_internal_format, &props);
if (ignore_checks || props.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)
{
std::vector<std::pair<u16, VkSubresourceLayout>> layout_alignment(tex.mipmap());
for (u32 i = 0; i < tex.mipmap(); ++i)
{
layout_alignment[i].first = 4096;
vkGetImageSubresourceLayout((*owner), m_image_contents, &subres, &layout_alignment[i].second);
if (m_view_type == VK_IMAGE_VIEW_TYPE_CUBE)
layout_alignment[i].second.size *= 6;
while (layout_alignment[i].first > 1)
{
//Test if is wholly divisible by alignment..
if (!(layout_alignment[i].second.rowPitch & (layout_alignment[i].first - 1)))
break;
layout_alignment[i].first >>= 1;
}
subres.mipLevel++;
}
if (tex.mipmap() == 1)
{
u64 buffer_size = get_placed_texture_storage_size(tex, layout_alignment[0].first, layout_alignment[0].first);
if (buffer_size != layout_alignment[0].second.size)
{
if (buffer_size > layout_alignment[0].second.size)
{
LOG_ERROR(RSX, "Layout->pitch = %d, size=%d, height=%d", layout_alignment[0].second.rowPitch, layout_alignment[0].second.size, tex.height());
LOG_ERROR(RSX, "Computed alignment would have been %d, which yielded a size of %d", layout_alignment[0].first, buffer_size);
LOG_ERROR(RSX, "Retrying...");
//layout_alignment[0].first >>= 1;
buffer_size = get_placed_texture_storage_size(tex, layout_alignment[0].first, layout_alignment[0].first);
if (buffer_size != layout_alignment[0].second.size)
throw EXCEPTION("Bad texture alignment computation!");
}
else
{
LOG_ERROR(RSX, "Bad texture alignment computation: expected size=%d bytes, computed=%d bytes, alignment=%d, hw pitch=%d",
layout_alignment[0].second.size, buffer_size, layout_alignment[0].first, layout_alignment[0].second.rowPitch);
}
}
CHECK_RESULT(vkMapMemory((*owner), vram_allocation, 0, m_memory_layout.size, 0, (void**)&data));
gsl::span<gsl::byte> mapped{ (gsl::byte*)(data + layout_alignment[0].second.offset), gsl::narrow<int>(layout_alignment[0].second.size) };
upload_placed_texture(mapped, tex, layout_alignment[0].first);
vkUnmapMemory((*owner), vram_allocation);
}
else
{
auto &layer_props = layout_alignment[layout_alignment.size() - 1].second;
u64 max_size = layer_props.offset + layer_props.size;
if (m_memory_layout.size < max_size)
{
throw EXCEPTION("Failed to upload texture. Invalid memory block size.");
}
int index= 0;
std::vector<std::pair<u64, u32>> layout_offset_info(tex.mipmap());
for (auto &mip_info : layout_offset_info)
{
auto &alignment = layout_alignment[index].first;
auto &layout = layout_alignment[index++].second;
mip_info = std::make_pair(layout.offset, (u32)layout.rowPitch);
}
CHECK_RESULT(vkMapMemory((*owner), vram_allocation, 0, m_memory_layout.size, 0, (void**)&data));
gsl::span<gsl::byte> mapped{ (gsl::byte*)(data), gsl::narrow<int>(m_memory_layout.size) };
upload_texture_mipmaps(mapped, tex, layout_offset_info);
vkUnmapMemory((*owner), vram_allocation);
}
}
else if (!ignore_checks)
{
if (!staging_texture)
{
staging_texture = new texture();
staging_texture->create((*owner), m_internal_format, VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_TILING_LINEAR, m_width, m_height, tex.mipmap(), false, default_component_map());
}
staging_texture->init(tex, cmd, true);
staging_texture->change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
ready = false;
}
}
void texture::init(rsx::texture &tex, vk::command_buffer &cmd)
{
init(tex, cmd, false);
}
void texture::flush(vk::command_buffer &cmd)
{
if (!ready)
{
vk::copy_texture(cmd, *staging_texture, *this, staging_texture->get_layout(), m_layout, m_width, m_height, m_mipmaps, m_image_aspect);
ready = true;
}
}
void texture::init_debug()
{
void *data;
CHECK_RESULT(vkMapMemory((*owner), vram_allocation, 0, m_memory_layout.size, 0, (void**)&data));
memset(data, 0xFF, m_memory_layout.size);
vkUnmapMemory((*owner), vram_allocation);
}
void texture::change_layout(vk::command_buffer &cmd, VkImageLayout new_layout)
{
if (m_layout == new_layout) return;
vk::change_image_layout(cmd, m_image_contents, m_layout, new_layout, m_image_aspect);
m_layout = new_layout;
}
VkImageLayout texture::get_layout()
{
return m_layout;
}
const u32 texture::width()
{
return m_width;
}
const u32 texture::height()
{
return m_height;
}
const u16 texture::mipmaps()
{
return m_mipmaps;
}
void texture::destroy()
{
if (!owner) return;
if (m_sampler)
vkDestroySampler((*owner), m_sampler, nullptr);
//Destroy all objects managed by this object
vkDestroyImageView((*owner), m_view, nullptr);
vkDestroyImage((*owner), m_image_contents, nullptr);
vram_allocation.destroy();
owner = nullptr;
m_sampler = nullptr;
m_view = nullptr;
m_image_contents = nullptr;
if (staging_texture)
{
staging_texture->destroy();
delete staging_texture;
staging_texture = nullptr;
}
}
const VkFormat texture::get_format()
{
return m_internal_format;
}
texture::operator VkImage()
{
return m_image_contents;
}
texture::operator VkImageView()
{
return m_view;
}
texture::operator VkSampler()
{
return m_sampler;
}
}

View File

@ -0,0 +1,266 @@
#pragma once
#include "stdafx.h"
#include "VKRenderTargets.h"
#include "VKGSRender.h"
#include "../Common/TextureUtils.h"
namespace vk
{
struct cached_texture_object
{
u32 native_rsx_address;
u32 native_rsx_size;
u16 width;
u16 height;
u16 depth;
u16 mipmaps;
vk::texture uploaded_texture;
u64 protected_rgn_start;
u64 protected_rgn_end;
bool exists = false;
bool locked = false;
bool dirty = true;
};
class texture_cache
{
private:
std::vector<cached_texture_object> m_cache;
u32 num_dirty_textures = 0;
bool lock_memory_region(u32 start, u32 size)
{
static const u32 memory_page_size = 4096;
start = start & ~(memory_page_size - 1);
size = (u32)align(size, memory_page_size);
return vm::page_protect(start, size, 0, 0, vm::page_writable);
}
bool unlock_memory_region(u32 start, u32 size)
{
static const u32 memory_page_size = 4096;
start = start & ~(memory_page_size - 1);
size = (u32)align(size, memory_page_size);
return vm::page_protect(start, size, 0, vm::page_writable, 0);
}
bool region_overlaps(u32 base1, u32 limit1, u32 base2, u32 limit2)
{
//Check for memory area overlap. unlock page(s) if needed and add this index to array.
//Axis separation test
const u32 &block_start = base1;
const u32 block_end = limit1;
if (limit2 < block_start) return false;
if (base2 > block_end) return false;
u32 min_separation = (limit2 - base2) + (limit1 - base1);
u32 range_limit = (block_end > limit2) ? block_end : limit2;
u32 range_base = (block_start < base2) ? block_start : base2;
u32 actual_separation = (range_limit - range_base);
if (actual_separation < min_separation)
return true;
return false;
}
cached_texture_object& find_cached_texture(u32 rsx_address, u32 rsx_size, bool confirm_dimensions = false, u16 width = 0, u16 height = 0, u16 mipmaps = 0)
{
for (cached_texture_object &tex : m_cache)
{
if (!tex.dirty && tex.exists &&
tex.native_rsx_address == rsx_address &&
tex.native_rsx_size == rsx_size)
{
if (!confirm_dimensions) return tex;
if (tex.width == width && tex.height == height && tex.mipmaps == mipmaps)
return tex;
else
{
LOG_ERROR(RSX, "Cached object for address 0x%X was found, but it does not match stored parameters.");
LOG_ERROR(RSX, "%d x %d vs %d x %d", width, height, tex.width, tex.height);
}
}
}
for (cached_texture_object &tex : m_cache)
{
if (tex.dirty)
{
if (tex.exists)
{
tex.uploaded_texture.destroy();
tex.exists = false;
}
return tex;
}
}
cached_texture_object object;
m_cache.push_back(object);
return m_cache[m_cache.size() - 1];
}
void lock_object(cached_texture_object &obj)
{
static const u32 memory_page_size = 4096;
obj.protected_rgn_start = obj.native_rsx_address & ~(memory_page_size - 1);
obj.protected_rgn_end = (u32)align(obj.native_rsx_size, memory_page_size);
obj.protected_rgn_end += obj.protected_rgn_start;
lock_memory_region(obj.protected_rgn_start, obj.native_rsx_size);
}
void unlock_object(cached_texture_object &obj)
{
unlock_memory_region(obj.protected_rgn_start, obj.native_rsx_size);
}
void purge_dirty_textures()
{
for (cached_texture_object &tex : m_cache)
{
if (tex.dirty && tex.exists)
{
tex.uploaded_texture.destroy();
tex.exists = false;
}
}
num_dirty_textures = 0;
}
public:
texture_cache() {}
~texture_cache() {}
void destroy()
{
for (cached_texture_object &tex : m_cache)
{
if (tex.exists)
{
tex.uploaded_texture.destroy();
tex.exists = false;
}
}
m_cache.resize(0);
}
vk::texture& upload_texture(command_buffer cmd, rsx::texture &tex, rsx::vk_render_targets &m_rtts)
{
if (num_dirty_textures > 32)
{
/**
* Should actually reuse available dirty textures whenever possible.
* For now, just remove them, from vram
*/
purge_dirty_textures();
}
const u32 texaddr = rsx::get_address(tex.offset(), tex.location());
const u32 range = (u32)get_texture_size(tex);
//First check if it exists as an rtt...
vk::texture *rtt_texture = nullptr;
if (rtt_texture = m_rtts.get_texture_from_render_target_if_applicable(texaddr))
{
return *rtt_texture;
}
if (rtt_texture = m_rtts.get_texture_from_depth_stencil_if_applicable(texaddr))
{
return *rtt_texture;
}
cached_texture_object& cto = find_cached_texture(texaddr, range, true, tex.width(), tex.height(), tex.mipmap());
if (cto.exists && !cto.dirty)
{
return cto.uploaded_texture;
}
u32 raw_format = tex.format();
u32 format = raw_format & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN);
VkComponentMapping mapping;
VkFormat vk_format = get_compatible_sampler_format(format, mapping, tex.remap());
cto.uploaded_texture.create(*vk::get_current_renderer(), vk_format, VK_IMAGE_USAGE_SAMPLED_BIT, tex.width(), tex.height(), tex.mipmap(), false, mapping);
cto.uploaded_texture.init(tex, cmd);
cto.uploaded_texture.change_layout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
cto.exists = true;
cto.dirty = false;
cto.native_rsx_address = texaddr;
cto.native_rsx_size = range;
cto.width = cto.uploaded_texture.width();
cto.height = cto.uploaded_texture.height();
cto.mipmaps = cto.uploaded_texture.mipmaps();
lock_object(cto);
return cto.uploaded_texture;
}
bool invalidate_address(u32 rsx_address)
{
for (cached_texture_object &tex : m_cache)
{
if (tex.dirty) continue;
if (rsx_address >= tex.protected_rgn_start &&
rsx_address < tex.protected_rgn_end)
{
unlock_object(tex);
num_dirty_textures++;
tex.native_rsx_address = 0;
tex.dirty = true;
return true;
}
}
return false;
}
void flush(vk::command_buffer &cmd)
{
//Finish all pending transactions for any cache managed textures..
for (cached_texture_object &tex : m_cache)
{
if (tex.dirty || !tex.exists) continue;
tex.uploaded_texture.flush(cmd);
}
}
void merge_dirty_textures(std::list<vk::texture> dirty_textures)
{
for (vk::texture &tex : dirty_textures)
{
cached_texture_object cto;
cto.uploaded_texture = tex;
cto.locked = false;
cto.exists = true;
cto.dirty = true;
cto.native_rsx_address = 0;
num_dirty_textures++;
m_cache.push_back(cto);
}
}
};
}

View File

@ -0,0 +1,565 @@
#include "stdafx.h"
#include "Utilities/rPlatform.h" // only for rImage
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/state.h"
#include "VKGSRender.h"
#include "../rsx_methods.h"
#include "../Common/BufferUtils.h"
namespace vk
{
bool requires_component_expansion(rsx::vertex_base_type type, u32 size)
{
if (size == 3)
{
switch (type)
{
case rsx::vertex_base_type::f:
return true;
}
}
return false;
}
u32 get_suitable_vk_size(rsx::vertex_base_type type, u32 size)
{
if (size == 3)
{
switch (type)
{
case rsx::vertex_base_type::f:
return 16;
}
}
return rsx::get_vertex_type_size_on_host(type, size);
}
VkFormat get_suitable_vk_format(rsx::vertex_base_type type, u8 size)
{
/**
* Set up buffer fetches to only work on 4-component access. This is hardware dependant so we use 4-component access to avoid branching based on IHV implementation
* AMD GCN 1.0 for example does not support RGB32 formats for texel buffers
*/
const VkFormat vec1_types[] = { VK_FORMAT_R16_UNORM, VK_FORMAT_R32_SFLOAT, VK_FORMAT_R16_SFLOAT, VK_FORMAT_R8_UNORM, VK_FORMAT_R16_SINT, VK_FORMAT_R16_SFLOAT, VK_FORMAT_R8_UNORM };
const VkFormat vec2_types[] = { VK_FORMAT_R16G16_UNORM, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R16G16_SFLOAT, VK_FORMAT_R8G8_UNORM, VK_FORMAT_R16G16_SINT, VK_FORMAT_R16G16_SFLOAT, VK_FORMAT_R8G8_UNORM };
const VkFormat vec3_types[] = { VK_FORMAT_R16G16B16A16_UNORM, VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R16G16B16A16_SINT, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R8G8B8A8_UNORM }; //VEC3 COMPONENTS NOT SUPPORTED!
const VkFormat vec4_types[] = { VK_FORMAT_R16G16B16A16_UNORM, VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R16G16B16A16_SINT, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R8G8B8A8_UNORM };
const VkFormat* vec_selectors[] = { 0, vec1_types, vec2_types, vec3_types, vec4_types };
if (type > rsx::vertex_base_type::ub256)
throw EXCEPTION("VKGS error: unknown vertex base type 0x%X.", (u32)type);
return vec_selectors[size][(int)type];
}
VkPrimitiveTopology get_appropriate_topology(rsx::primitive_type& mode, bool &requires_modification)
{
requires_modification = false;
switch (mode)
{
case rsx::primitive_type::lines:
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
case rsx::primitive_type::line_loop:
requires_modification = true;
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
case rsx::primitive_type::line_strip:
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
case rsx::primitive_type::points:
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
case rsx::primitive_type::triangles:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case rsx::primitive_type::triangle_strip:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
case rsx::primitive_type::triangle_fan:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
case rsx::primitive_type::quads:
case rsx::primitive_type::quad_strip:
case rsx::primitive_type::polygon:
requires_modification = true;
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
default:
throw ("Unsupported primitive topology 0x%X", (u8)mode);
}
}
/**
* Expand line loop array to line strip array; simply loop back the last vertex to the first..
*/
u32 expand_line_loop_array_to_strip(u32 vertex_draw_count, std::vector<u16>& indices)
{
u32 i = 0;
indices.resize(vertex_draw_count + 1);
for (; i < vertex_draw_count; ++i)
indices[i] = i;
indices[i] = 0;
return indices.size();
}
template<typename T>
u32 expand_indexed_line_loop_to_strip(u32 original_count, const T* original_indices, std::vector<T>& indices)
{
indices.resize(original_count + 1);
u32 i = 0;
for (; i < original_count; ++i)
indices[i] = original_indices[i];
indices[i] = original_indices[0];
return indices.size();
}
/**
* Template: Expand any N-compoent vector to a larger X-component vector and pad unused slots with 1
*/
template<typename T, u8 src_components, u8 dst_components, u32 padding>
void expand_array_components(const T* src_data, std::vector<u8>& dst_data, u32 vertex_count)
{
u32 dst_size = (vertex_count * dst_components * sizeof(T));
dst_data.resize(dst_size);
T* src = const_cast<T*>(src_data);
T* dst = reinterpret_cast<T*>(dst_data.data());
for (u32 index = 0; index < vertex_count; ++index)
{
for (u8 channel = 0; channel < dst_components; channel++)
{
if (channel < src_components)
{
*dst = *src;
dst++;
src++;
}
else
{
*dst = (T)(padding);
dst++;
}
}
}
}
template <typename T, u32 padding>
void copy_inlined_data_to_buffer(void *src_data, void *dst_data, u32 vertex_count, rsx::vertex_base_type type, u8 src_channels, u8 dst_channels, u16 element_size, u16 stride)
{
u8 *src = static_cast<u8*>(src_data);
u8 *dst = static_cast<u8*>(dst_data);
for (u32 i = 0; i < vertex_count; ++i)
{
T* src_ptr = reinterpret_cast<T*>(src);
T* dst_ptr = reinterpret_cast<T*>(dst);
switch (type)
{
case rsx::vertex_base_type::ub:
{
if (src_channels == 4)
{
dst[0] = src[3];
dst[1] = src[2];
dst[2] = src[1];
dst[3] = src[0];
break;
}
}
default:
{
for (u8 ch = 0; ch < dst_channels; ++ch)
{
if (ch < src_channels)
{
*dst_ptr = *src_ptr;
src_ptr++;
}
else
*dst_ptr = (T)(padding);
dst_ptr++;
}
}
}
src += stride;
dst += element_size;
}
}
void prepare_buffer_for_writing(void *data, rsx::vertex_base_type type, u8 vertex_size, u32 vertex_count)
{
switch (type)
{
case rsx::vertex_base_type::sf:
{
if (vertex_size == 3)
{
/**
* Pad the 4th component for half-float arrays to 1, since texelfetch does not mask components
*/
u16 *dst = reinterpret_cast<u16*>(data);
for (u32 i = 0, idx = 3; i < vertex_count; ++i, idx += 4)
dst[idx] = 0x3c00;
}
break;
}
}
}
}
std::tuple<VkPrimitiveTopology, bool, u32, VkIndexType>
VKGSRender::upload_vertex_data()
{
//initialize vertex attributes
std::vector<u8> vertex_arrays_data;
const std::string reg_table[] =
{
"in_pos_buffer", "in_weight_buffer", "in_normal_buffer",
"in_diff_color_buffer", "in_spec_color_buffer",
"in_fog_buffer",
"in_point_size_buffer", "in_7_buffer",
"in_tc0_buffer", "in_tc1_buffer", "in_tc2_buffer", "in_tc3_buffer",
"in_tc4_buffer", "in_tc5_buffer", "in_tc6_buffer", "in_tc7_buffer"
};
u32 input_mask = rsx::method_registers[NV4097_SET_VERTEX_ATTRIB_INPUT_MASK];
std::vector<u8> vertex_index_array;
vertex_draw_count = 0;
u32 min_index, max_index;
if (draw_command == rsx::draw_command::indexed)
{
rsx::index_array_type type = rsx::to_index_array_type(rsx::method_registers[NV4097_SET_INDEX_ARRAY_DMA] >> 4);
u32 type_size = gsl::narrow<u32>(get_index_type_size(type));
for (const auto& first_count : first_count_commands)
{
vertex_draw_count += first_count.second;
}
vertex_index_array.resize(vertex_draw_count * type_size);
switch (type)
{
case rsx::index_array_type::u32:
std::tie(min_index, max_index) = write_index_array_data_to_buffer_untouched(gsl::span<u32>((u32*)vertex_index_array.data(), vertex_draw_count), first_count_commands);
break;
case rsx::index_array_type::u16:
std::tie(min_index, max_index) = write_index_array_data_to_buffer_untouched(gsl::span<u16>((u16*)vertex_index_array.data(), vertex_draw_count), first_count_commands);
break;
}
}
if (draw_command == rsx::draw_command::inlined_array)
{
u32 stride = 0;
u32 offsets[rsx::limits::vertex_count] = { 0 };
for (u32 i = 0; i < rsx::limits::vertex_count; ++i)
{
const auto &info = vertex_arrays_info[i];
if (!info.size) continue;
offsets[i] = stride;
stride += rsx::get_vertex_type_size_on_host(info.type, info.size);
}
vertex_draw_count = (u32)(inline_vertex_array.size() * sizeof(u32)) / stride;
for (int index = 0; index < rsx::limits::vertex_count; ++index)
{
auto &vertex_info = vertex_arrays_info[index];
if (!m_program->has_uniform(vk::glsl::glsl_vertex_program, reg_table[index]))
continue;
if (!vertex_info.size) // disabled
{
m_program->bind_uniform(vk::glsl::glsl_vertex_program, reg_table[index]);
continue;
}
const u32 element_size = vk::get_suitable_vk_size(vertex_info.type, vertex_info.size);
const u32 data_size = element_size * vertex_draw_count;
const VkFormat format = vk::get_suitable_vk_format(vertex_info.type, vertex_info.size);
vertex_arrays_data.resize(data_size);
u8 *src = reinterpret_cast<u8*>(inline_vertex_array.data());
u8 *dst = vertex_arrays_data.data();
src += offsets[index];
u8 opt_size = vertex_info.size;
if (vertex_info.size == 3)
opt_size = 4;
//TODO: properly handle cmp type
if (vertex_info.type == rsx::vertex_base_type::cmp)
LOG_ERROR(RSX, "Compressed vertex attributes not supported for inlined arrays yet");
switch (vertex_info.type)
{
case rsx::vertex_base_type::f:
vk::copy_inlined_data_to_buffer<float, 1>(src, dst, vertex_draw_count, vertex_info.type, vertex_info.size, opt_size, element_size, stride);
break;
case rsx::vertex_base_type::sf:
vk::copy_inlined_data_to_buffer<u16, 0x3c00>(src, dst, vertex_draw_count, vertex_info.type, vertex_info.size, opt_size, element_size, stride);
break;
case rsx::vertex_base_type::s1:
case rsx::vertex_base_type::ub:
case rsx::vertex_base_type::ub256:
vk::copy_inlined_data_to_buffer<u8, 1>(src, dst, vertex_draw_count, vertex_info.type, vertex_info.size, opt_size, element_size, stride);
break;
case rsx::vertex_base_type::s32k:
case rsx::vertex_base_type::cmp:
vk::copy_inlined_data_to_buffer<u16, 1>(src, dst, vertex_draw_count, vertex_info.type, vertex_info.size, opt_size, element_size, stride);
break;
default:
throw EXCEPTION("Unknown base type %d", vertex_info.type);
}
auto &buffer = m_attrib_buffers[index];
buffer.sub_data(0, data_size, vertex_arrays_data.data());
buffer.set_format(format);
//Link texture to uniform location
m_program->bind_uniform(vk::glsl::glsl_vertex_program, reg_table[index], buffer, true);
}
}
if (draw_command == rsx::draw_command::array)
{
for (const auto &first_count : first_count_commands)
{
vertex_draw_count += first_count.second;
}
}
if (draw_command == rsx::draw_command::array || draw_command == rsx::draw_command::indexed)
{
for (int index = 0; index < rsx::limits::vertex_count; ++index)
{
if (!m_program->has_uniform(vk::glsl::glsl_vertex_program, reg_table[index]))
continue;
bool enabled = !!(input_mask & (1 << index));
if (!enabled)
{
m_program->bind_uniform(vk::glsl::glsl_vertex_program, reg_table[index]);
continue;
}
if (vertex_arrays_info[index].size > 0)
{
auto &vertex_info = vertex_arrays_info[index];
// Active vertex array
std::vector<gsl::byte> vertex_array;
// Fill vertex_array
u32 element_size = rsx::get_vertex_type_size_on_host(vertex_info.type, vertex_info.size);
vertex_array.resize(vertex_draw_count * element_size);
// Get source pointer
u32 base_offset = rsx::method_registers[NV4097_SET_VERTEX_DATA_BASE_OFFSET];
u32 offset = rsx::method_registers[NV4097_SET_VERTEX_DATA_ARRAY_OFFSET + index];
u32 address = base_offset + rsx::get_address(offset & 0x7fffffff, offset >> 31);
const gsl::byte *src_ptr = gsl::narrow_cast<const gsl::byte*>(vm::base(address));
u32 num_stored_verts = vertex_draw_count;
if (draw_command == rsx::draw_command::array)
{
size_t offset = 0;
gsl::span<gsl::byte> dest_span(vertex_array);
vk::prepare_buffer_for_writing(vertex_array.data(), vertex_info.type, vertex_info.size, vertex_draw_count);
for (const auto &first_count : first_count_commands)
{
write_vertex_array_data_to_buffer(dest_span.subspan(offset), src_ptr, first_count.first, first_count.second, vertex_info.type, vertex_info.size, vertex_info.stride);
offset += first_count.second * element_size;
}
}
if (draw_command == rsx::draw_command::indexed)
{
num_stored_verts = (max_index + 1);
vertex_array.resize((max_index + 1) * element_size);
gsl::span<gsl::byte> dest_span(vertex_array);
vk::prepare_buffer_for_writing(vertex_array.data(), vertex_info.type, vertex_info.size, vertex_draw_count);
write_vertex_array_data_to_buffer(dest_span, src_ptr, 0, max_index + 1, vertex_info.type, vertex_info.size, vertex_info.stride);
}
std::vector<u8> converted_buffer;
void *data_ptr = vertex_array.data();
if (vk::requires_component_expansion(vertex_info.type, vertex_info.size))
{
switch (vertex_info.type)
{
case rsx::vertex_base_type::f:
vk::expand_array_components<float, 3, 4, 1>(reinterpret_cast<float*>(vertex_array.data()), converted_buffer, num_stored_verts);
break;
}
data_ptr = static_cast<void*>(converted_buffer.data());
}
const VkFormat format = vk::get_suitable_vk_format(vertex_info.type, vertex_info.size);
const u32 data_size = vk::get_suitable_vk_size(vertex_info.type, vertex_info.size) * num_stored_verts;
auto &buffer = m_attrib_buffers[index];
buffer.sub_data(0, data_size, data_ptr);
buffer.set_format(format);
m_program->bind_uniform(vk::glsl::glsl_vertex_program, reg_table[index], buffer, true);
}
else if (register_vertex_info[index].size > 0)
{
//Untested!
auto &vertex_data = register_vertex_data[index];
auto &vertex_info = register_vertex_info[index];
switch (vertex_info.type)
{
case rsx::vertex_base_type::f:
{
size_t data_size = vertex_data.size();
const VkFormat format = vk::get_suitable_vk_format(vertex_info.type, vertex_info.size);
std::vector<u8> converted_buffer;
void *data_ptr = vertex_data.data();
if (vk::requires_component_expansion(vertex_info.type, vertex_info.size))
{
switch (vertex_info.type)
{
case rsx::vertex_base_type::f:
{
const u32 num_stored_verts = data_size / (sizeof(float) * vertex_info.size);
vk::expand_array_components<float, 3, 4, 1>(reinterpret_cast<float*>(vertex_data.data()), converted_buffer, num_stored_verts);
break;
}
}
data_ptr = static_cast<void*>(converted_buffer.data());
data_size = converted_buffer.size();
}
auto &buffer = m_attrib_buffers[index];
buffer.sub_data(0, data_size, data_ptr);
buffer.set_format(format);
m_program->bind_uniform(vk::glsl::glsl_vertex_program, reg_table[index], buffer, true);
break;
}
default:
LOG_ERROR(RSX, "bad non array vertex data format (type = %d, size = %d)", vertex_info.type, vertex_info.size);
break;
}
}
}
}
bool is_indexed_draw = (draw_command == rsx::draw_command::indexed);
bool index_buffer_filled = false;
bool primitives_emulated = false;
u32 index_count = vertex_draw_count;
VkIndexType index_format = VK_INDEX_TYPE_UINT16;
VkPrimitiveTopology prims = vk::get_appropriate_topology(draw_mode, primitives_emulated);
if (primitives_emulated)
{
//Line loops are line-strips with loop-back; using line-strips-with-adj doesnt work for vulkan
if (draw_mode == rsx::primitive_type::line_loop)
{
std::vector<u16> indices;
if (!is_indexed_draw)
{
index_count = vk::expand_line_loop_array_to_strip(vertex_draw_count, indices);
m_index_buffer.sub_data(0, index_count*sizeof(u16), indices.data());
}
else
{
rsx::index_array_type indexed_type = rsx::to_index_array_type(rsx::method_registers[NV4097_SET_INDEX_ARRAY_DMA] >> 4);
if (indexed_type == rsx::index_array_type::u32)
{
index_format = VK_INDEX_TYPE_UINT32;
std::vector<u32> indices32;
index_count = vk::expand_indexed_line_loop_to_strip(vertex_draw_count, (u32*)vertex_index_array.data(), indices32);
m_index_buffer.sub_data(0, index_count*sizeof(u32), indices32.data());
}
else
{
index_count = vk::expand_indexed_line_loop_to_strip(vertex_draw_count, (u16*)vertex_index_array.data(), indices);
m_index_buffer.sub_data(0, index_count*sizeof(u16), indices.data());
}
}
}
else
{
index_count = get_index_count(draw_mode, vertex_draw_count);
std::vector<u16> indices(index_count);
if (is_indexed_draw)
{
rsx::index_array_type indexed_type = rsx::to_index_array_type(rsx::method_registers[NV4097_SET_INDEX_ARRAY_DMA] >> 4);
size_t index_size = get_index_type_size(indexed_type);
std::vector<std::pair<u32, u32>> ranges;
ranges.push_back(std::pair<u32, u32>(0, vertex_draw_count));
gsl::span<u16> dst = { (u16*)indices.data(), gsl::narrow<int>(index_count) };
write_index_array_data_to_buffer(dst, draw_mode, ranges);
}
else
{
write_index_array_for_non_indexed_non_native_primitive_to_buffer(reinterpret_cast<char*>(indices.data()), draw_mode, 0, vertex_draw_count);
}
m_index_buffer.sub_data(0, index_count * sizeof(u16), indices.data());
}
is_indexed_draw = true;
index_buffer_filled = true;
}
if (!index_buffer_filled && is_indexed_draw)
{
rsx::index_array_type indexed_type = rsx::to_index_array_type(rsx::method_registers[NV4097_SET_INDEX_ARRAY_DMA] >> 4);
index_format = VK_INDEX_TYPE_UINT16;
VkFormat fmt = VK_FORMAT_R16_UINT;
u32 elem_size = get_index_type_size(indexed_type);
if (indexed_type == rsx::index_array_type::u32)
{
index_format = VK_INDEX_TYPE_UINT32;
fmt = VK_FORMAT_R32_UINT;
}
u32 index_sz = vertex_index_array.size() / elem_size;
if (index_sz != vertex_draw_count)
LOG_ERROR(RSX, "Vertex draw count mismatch!");
m_index_buffer.sub_data(0, vertex_index_array.size(), vertex_index_array.data());
m_index_buffer.set_format(fmt); //Unnecessary unless viewing contents in sampler...
}
return std::make_tuple(prims, is_indexed_draw, index_count, index_format);
}

View File

@ -0,0 +1,301 @@
#include "stdafx.h"
#include "Emu/System.h"
#include "VKVertexProgram.h"
#include "VKCommonDecompiler.h"
#include "VKHelpers.h"
std::string VKVertexDecompilerThread::getFloatTypeName(size_t elementCount)
{
return vk::getFloatTypeNameImpl(elementCount);
}
std::string VKVertexDecompilerThread::getIntTypeName(size_t elementCount)
{
return "ivec4";
}
std::string VKVertexDecompilerThread::getFunction(FUNCTION f)
{
return vk::getFunctionImpl(f);
}
std::string VKVertexDecompilerThread::compareFunction(COMPARE f, const std::string &Op0, const std::string &Op1)
{
return vk::compareFunctionImpl(f, Op0, Op1);
}
void VKVertexDecompilerThread::insertHeader(std::stringstream &OS)
{
OS << "#version 450" << std::endl << std::endl;
OS << "#extension GL_ARB_separate_shader_objects : enable" << std::endl;
OS << "layout(std140, set=0, binding = 0) uniform ScaleOffsetBuffer" << std::endl;
OS << "{" << std::endl;
OS << " mat4 scaleOffsetMat;" << std::endl;
OS << " float fog_param0;\n";
OS << " float fog_param1;\n";
OS << "};" << std::endl;
vk::glsl::program_input in;
in.location = 0;
in.domain = vk::glsl::glsl_vertex_program;
in.name = "ScaleOffsetBuffer";
in.type = vk::glsl::input_type_uniform_buffer;
inputs.push_back(in);
}
void VKVertexDecompilerThread::insertInputs(std::stringstream & OS, const std::vector<ParamType>& inputs)
{
std::vector<std::tuple<size_t, std::string>> input_data;
for (const ParamType &PT : inputs)
{
for (const ParamItem &PI : PT.items)
{
input_data.push_back(std::make_tuple(PI.location, PI.name));
}
}
/**
* Its is important that the locations are in the order that vertex attributes are expected.
* If order is not adhered to, channels may be swapped leading to corruption
*/
std::sort(input_data.begin(), input_data.end());
int location = 2;
for (const std::tuple<size_t, std::string> item : input_data)
{
for (const ParamType &PT : inputs)
{
for (const ParamItem &PI : PT.items)
{
if (PI.name == std::get<1>(item))
{
vk::glsl::program_input in;
in.location = location;
in.domain = vk::glsl::glsl_vertex_program;
in.name = PI.name + "_buffer";
in.type = vk::glsl::input_type_texel_buffer;
this->inputs.push_back(in);
OS << "layout(set=0, binding=" << location++ << ")" << " uniform samplerBuffer" << " " << PI.name << "_buffer;" << std::endl;
}
}
}
}
}
void VKVertexDecompilerThread::insertConstants(std::stringstream & OS, const std::vector<ParamType> & constants)
{
OS << "layout(std140, set=0, binding = 1) uniform VertexConstantsBuffer" << std::endl;
OS << "{" << std::endl;
OS << " vec4 vc[468];" << std::endl;
OS << "};" << std::endl;
vk::glsl::program_input in;
in.location = 1;
in.domain = vk::glsl::glsl_vertex_program;
in.name = "VertexConstantsBuffer";
in.type = vk::glsl::input_type_uniform_buffer;
inputs.push_back(in);
}
struct reg_info
{
std::string name;
bool need_declare;
std::string src_reg;
std::string src_reg_mask;
bool need_cast;
};
static const reg_info reg_table[] =
{
{ "gl_Position", false, "dst_reg0", "", false },
{ "diff_color", true, "dst_reg1", "", false },
{ "spec_color", true, "dst_reg2", "", false },
{ "front_diff_color", true, "dst_reg3", "", false },
{ "front_spec_color", true, "dst_reg4", "", false },
{ "fog_c", true, "dst_reg5", ".xxxx", true },
{ "gl_ClipDistance[0]", false, "dst_reg5", ".y", false },
{ "gl_ClipDistance[1]", false, "dst_reg5", ".z", false },
{ "gl_ClipDistance[2]", false, "dst_reg5", ".w", false },
{ "gl_PointSize", false, "dst_reg6", ".x", false },
{ "gl_ClipDistance[3]", false, "dst_reg6", ".y", false },
{ "gl_ClipDistance[4]", false, "dst_reg6", ".z", false },
{ "gl_ClipDistance[5]", false, "dst_reg6", ".w", false },
{ "tc0", true, "dst_reg7", "", false },
{ "tc1", true, "dst_reg8", "", false },
{ "tc2", true, "dst_reg9", "", false },
{ "tc3", true, "dst_reg10", "", false },
{ "tc4", true, "dst_reg11", "", false },
{ "tc5", true, "dst_reg12", "", false },
{ "tc6", true, "dst_reg13", "", false },
{ "tc7", true, "dst_reg14", "", false },
{ "tc8", true, "dst_reg15", "", false },
{ "tc9", true, "dst_reg6", "", false } // In this line, dst_reg6 is correct since dst_reg goes from 0 to 15.
};
void VKVertexDecompilerThread::insertOutputs(std::stringstream & OS, const std::vector<ParamType> & outputs)
{
for (auto &i : reg_table)
{
if (m_parr.HasParam(PF_PARAM_NONE, "vec4", i.src_reg) && i.need_declare)
{
const vk::varying_register_t &reg = vk::get_varying_register(i.name);
// if (i.name == "fogc")
// OS << "layout(location=" << reg.reg_location << ") out vec4 fog_c;" << std::endl;
// else
OS << "layout(location=" << reg.reg_location << ") out vec4 " << i.name << ";" << std::endl;
}
}
}
namespace vk
{
void add_input(std::stringstream & OS, const ParamItem &PI, const std::vector<rsx_vertex_input> &inputs)
{
for (const auto &real_input : inputs)
{
if (real_input.location != PI.location)
continue;
if (!real_input.is_array)
{
OS << " vec4 " << PI.name << " = texelFetch(" << PI.name << "_buffer, 0);" << std::endl;
return;
}
if (real_input.frequency > 1)
{
if (real_input.is_modulo)
{
OS << " vec4 " << PI.name << "= texelFetch(" << PI.name << "_buffer, gl_VertexIndex %" << real_input.frequency << ");" << std::endl;
return;
}
OS << " vec4 " << PI.name << "= texelFetch(" << PI.name << "_buffer, gl_VertexIndex /" << real_input.frequency << ");" << std::endl;
return;
}
OS << " vec4 " << PI.name << "= texelFetch(" << PI.name << "_buffer, gl_VertexIndex).rgba;" << std::endl;
return;
}
OS << " vec4 " << PI.name << " = vec4(0., 0., 0., 1.);" << std::endl;
}
}
void VKVertexDecompilerThread::insertMainStart(std::stringstream & OS)
{
vk::insert_glsl_legacy_function(OS);
OS << "void main()" << std::endl;
OS << "{" << std::endl;
// Declare inside main function
for (const ParamType PT : m_parr.params[PF_PARAM_NONE])
{
for (const ParamItem &PI : PT.items)
{
OS << " " << PT.type << " " << PI.name;
if (!PI.value.empty())
OS << " = " << PI.value;
OS << ";" << std::endl;
}
}
for (const ParamType &PT : m_parr.params[PF_PARAM_IN])
{
for (const ParamItem &PI : PT.items)
vk::add_input(OS, PI, rsx_vertex_program.rsx_vertex_inputs);
}
}
void VKVertexDecompilerThread::insertMainEnd(std::stringstream & OS)
{
for (auto &i : reg_table)
{
if (m_parr.HasParam(PF_PARAM_NONE, "vec4", i.src_reg))
OS << " " << i.name << " = " << i.src_reg << i.src_reg_mask << ";" << std::endl;
}
OS << " gl_Position = gl_Position * scaleOffsetMat;" << std::endl;
OS << "}" << std::endl;
}
void VKVertexDecompilerThread::Task()
{
m_shader = Decompile();
vk_prog->SetInputs(inputs);
}
VKVertexProgram::VKVertexProgram()
{
}
VKVertexProgram::~VKVertexProgram()
{
Delete();
}
void VKVertexProgram::Decompile(const RSXVertexProgram& prog)
{
VKVertexDecompilerThread decompiler(prog, shader, parr, *this);
decompiler.Task();
}
void VKVertexProgram::Compile()
{
fs::file(fs::get_config_dir() + "VertexProgram.vert", fom::rewrite).write(shader);
std::vector<u32> spir_v;
if (!vk::compile_glsl_to_spv(shader, vk::glsl::glsl_vertex_program, spir_v))
throw EXCEPTION("Failed to compile vertex shader");
VkShaderModuleCreateInfo vs_info;
vs_info.codeSize = spir_v.size() * sizeof(u32);
vs_info.pNext = nullptr;
vs_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
vs_info.pCode = (uint32_t*)spir_v.data();
vs_info.flags = 0;
VkDevice dev = (VkDevice)*vk::get_current_renderer();
vkCreateShaderModule(dev, &vs_info, nullptr, &handle);
id = (u32)((u64)handle);
}
void VKVertexProgram::Delete()
{
shader.clear();
if (handle)
{
if (Emu.IsStopped())
{
LOG_WARNING(RSX, "VKVertexProgram::Delete(): vkDestroyShaderModule(0x%X) avoided", handle);
}
else
{
VkDevice dev = (VkDevice)*vk::get_current_renderer();
vkDestroyShaderModule(dev, handle, nullptr);
}
handle = nullptr;
}
}
void VKVertexProgram::SetInputs(std::vector<vk::glsl::program_input>& inputs)
{
for (auto &it : inputs)
{
uniforms.push_back(it);
}
}

View File

@ -0,0 +1,58 @@
#pragma once
#include "../Common/VertexProgramDecompiler.h"
#include "Emu/RSX/RSXVertexProgram.h"
#include "Utilities/Thread.h"
#include "VulkanAPI.h"
#include "../VK/VKHelpers.h"
struct VKVertexDecompilerThread : public VertexProgramDecompiler
{
std::string &m_shader;
std::vector<vk::glsl::program_input> inputs;
class VKVertexProgram *vk_prog;
protected:
virtual std::string getFloatTypeName(size_t elementCount) override;
std::string getIntTypeName(size_t elementCount) override;
virtual std::string getFunction(FUNCTION) override;
virtual std::string compareFunction(COMPARE, const std::string&, const std::string&) override;
virtual void insertHeader(std::stringstream &OS) override;
virtual void insertInputs(std::stringstream &OS, const std::vector<ParamType> &inputs) override;
virtual void insertConstants(std::stringstream &OS, const std::vector<ParamType> &constants) override;
virtual void insertOutputs(std::stringstream &OS, const std::vector<ParamType> &outputs) override;
virtual void insertMainStart(std::stringstream &OS) override;
virtual void insertMainEnd(std::stringstream &OS) override;
const RSXVertexProgram &rsx_vertex_program;
public:
VKVertexDecompilerThread(const RSXVertexProgram &prog, std::string& shader, ParamArray& parr, class VKVertexProgram &dst)
: VertexProgramDecompiler(prog)
, m_shader(shader)
, rsx_vertex_program(prog)
, vk_prog(&dst)
{
}
void Task();
const std::vector<vk::glsl::program_input>& get_inputs() { return inputs; }
};
class VKVertexProgram
{
public:
VKVertexProgram();
~VKVertexProgram();
ParamArray parr;
VkShaderModule handle = nullptr;
u32 id;
std::string shader;
std::vector<vk::glsl::program_input> uniforms;
void Decompile(const RSXVertexProgram& prog);
void Compile();
void SetInputs(std::vector<vk::glsl::program_input>& inputs);
private:
void Delete();
};

View File

@ -0,0 +1 @@
#include "stdafx.h"

View File

@ -0,0 +1,16 @@
#pragma once
#ifdef _WIN32
#define VK_USE_PLATFORM_WIN32_KHR
#elif !defined __APPLE__
#define VK_USE_PLATFORM_XLIB_KHR
#endif
#include <vulkan/vulkan.h>
#include <vulkan/vk_sdk_platform.h>
#include "Utilities/types.h"
namespace vk
{
void init();
}

View File

@ -250,6 +250,8 @@ SettingsDialog::SettingsDialog(wxWindow *parent, rpcs3::config_t* cfg)
cbox_gs_d3d_adaptater->Enable(false);
chbox_gs_overlay->Enable(false);
}
cbox_gs_render->Append("Vulkan");
#endif
for (int i = 1; i < WXSIZEOF(ResolutionTable); ++i)

View File

@ -1,125 +1,141 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug - LLVM|x64">
<Configuration>Debug - LLVM</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug - MemLeak|x64">
<Configuration>Debug - MemLeak</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release - LLVM|x64">
<Configuration>Release - LLVM</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Emu\RSX\VK\VKGSRender.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Emu\RSX\VK\VKGSRender.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="emucore.vcxproj">
<Project>{c4a10229-4712-4bd2-b63e-50d93c67a038}</Project>
</ProjectReference>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{3EE5F075-B546-42C4-B6A8-E3CCEF38B78D}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>VKGSRender</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\rpcs3_default.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="..\rpcs3_debug.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug - MemLeak|x64'" Label="PropertySheets">
<Import Project="..\rpcs3_debug.props" />
<Import Project="..\rpcs3_memleak.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'" Label="PropertySheets">
<Import Project="..\rpcs3_debug.props" />
<Import Project="..\rpcs3_llvm.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="..\rpcs3_release.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'" Label="PropertySheets">
<Import Project="..\rpcs3_release.props" />
<Import Project="..\rpcs3_llvm.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\Vulkan\Vulkan-LoaderAndValidationLayers\include;..\Vulkan\glslang\glslang\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\Vulkan\Vulkan-LoaderAndValidationLayers\include;..\Vulkan\glslang\glslang\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">
<ClCompile>
<AdditionalIncludeDirectories>..\Vulkan\Vulkan-LoaderAndValidationLayers\include;..\Vulkan\glslang\glslang\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug - MemLeak|x64'">
<ClCompile>
<AdditionalIncludeDirectories>..\Vulkan\Vulkan-LoaderAndValidationLayers\include;..\Vulkan\glslang\glslang\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">
<ClCompile>
<AdditionalIncludeDirectories>..\Vulkan\Vulkan-LoaderAndValidationLayers\include;..\Vulkan\glslang\glslang\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug - LLVM|x64">
<Configuration>Debug - LLVM</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug - MemLeak|x64">
<Configuration>Debug - MemLeak</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release - LLVM|x64">
<Configuration>Release - LLVM</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Emu\RSX\VK\VKCommonDecompiler.h" />
<ClInclude Include="Emu\RSX\VK\VKFragmentProgram.h" />
<ClInclude Include="Emu\RSX\VK\VKGSRender.h" />
<ClInclude Include="Emu\RSX\VK\VKHelpers.h" />
<ClInclude Include="Emu\RSX\VK\VKProgramBuffer.h" />
<ClInclude Include="Emu\RSX\VK\VKRenderTargets.h" />
<ClInclude Include="Emu\RSX\VK\VKTextureCache.h" />
<ClInclude Include="Emu\RSX\VK\VKVertexProgram.h" />
<ClInclude Include="Emu\RSX\VK\VulkanAPI.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Emu\RSX\VK\VKCommonDecompiler.cpp" />
<ClCompile Include="Emu\RSX\VK\VKFragmentProgram.cpp" />
<ClCompile Include="Emu\RSX\VK\VKGSRender.cpp" />
<ClCompile Include="Emu\RSX\VK\VKHelpers.cpp" />
<ClCompile Include="Emu\RSX\VK\VKProgramPipeline.cpp" />
<ClCompile Include="Emu\RSX\VK\VKTexture.cpp" />
<ClCompile Include="Emu\RSX\VK\VKVertexBuffers.cpp" />
<ClCompile Include="Emu\RSX\VK\VKVertexProgram.cpp" />
<ClCompile Include="Emu\RSX\VK\VulkanAPI.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="emucore.vcxproj">
<Project>{c4a10229-4712-4bd2-b63e-50d93c67a038}</Project>
</ProjectReference>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{3EE5F075-B546-42C4-B6A8-E3CCEF38B78D}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>VKGSRender</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\rpcs3_default.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="..\rpcs3_debug.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug - MemLeak|x64'" Label="PropertySheets">
<Import Project="..\rpcs3_debug.props" />
<Import Project="..\rpcs3_memleak.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'" Label="PropertySheets">
<Import Project="..\rpcs3_debug.props" />
<Import Project="..\rpcs3_llvm.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="..\rpcs3_release.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'" Label="PropertySheets">
<Import Project="..\rpcs3_release.props" />
<Import Project="..\rpcs3_llvm.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\Vulkan\Vulkan-LoaderAndValidationLayers\include;..\Vulkan\glslang\glslang\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\Vulkan\Vulkan-LoaderAndValidationLayers\include;..\Vulkan\glslang\glslang\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">
<ClCompile>
<AdditionalIncludeDirectories>..\Vulkan\Vulkan-LoaderAndValidationLayers\include;..\Vulkan\glslang\glslang\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug - MemLeak|x64'">
<ClCompile>
<AdditionalIncludeDirectories>..\Vulkan\Vulkan-LoaderAndValidationLayers\include;..\Vulkan\glslang\glslang\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">
<ClCompile>
<AdditionalIncludeDirectories>..\Vulkan\Vulkan-LoaderAndValidationLayers\include;..\Vulkan\glslang\glslang\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -1,19 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Emu\RSX\VK\VKGSRender.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Emu\RSX\VK\VKGSRender.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Emu\RSX\VK\VKGSRender.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\VKCommonDecompiler.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\VKFragmentProgram.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\VKHelpers.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\VKProgramBuffer.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\VKRenderTargets.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\VKTextureCache.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\VKVertexProgram.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\VulkanAPI.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Emu\RSX\VK\VKGSRender.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\VKCommonDecompiler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\VKFragmentProgram.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\VKHelpers.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\VKProgramPipeline.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\VKTexture.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\VKVertexProgram.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\VulkanAPI.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\VKVertexBuffers.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -50,7 +50,8 @@ enum class rsx_renderer_type
{
Null,
OpenGL,
DX12
DX12,
Vulkan
};
enum class rsx_aspect_ratio
@ -93,6 +94,7 @@ namespace convert
case rsx_renderer_type::Null: return "Null";
case rsx_renderer_type::OpenGL: return "OpenGL";
case rsx_renderer_type::DX12: return "DX12";
case rsx_renderer_type::Vulkan: return "Vulkan";
}
return "Unknown";
@ -113,6 +115,9 @@ namespace convert
if (value == "DX12")
return rsx_renderer_type::DX12;
if (value == "Vulkan")
return rsx_renderer_type::Vulkan;
return rsx_renderer_type::Null;
}
};

View File

@ -35,6 +35,7 @@
#include "Emu/Audio/Null/NullAudioThread.h"
#include "Emu/Audio/AL/OpenALThread.h"
#ifdef _MSC_VER
#include "Emu/RSX/VK/VKGSRender.h"
#include "Emu/RSX/D3D12/D3D12GSRender.h"
#include "Emu/Audio/XAudio2/XAudio2Thread.h"
#endif
@ -129,6 +130,7 @@ bool Rpcs3App::OnInit()
case frame_type::OpenGL: return std::make_unique<GLGSFrame>();
case frame_type::DX12: return std::make_unique<GSFrame>("DirectX 12");
case frame_type::Null: return std::make_unique<GSFrame>("Null");
case frame_type::Vulkan: return std::make_unique<GSFrame>("Vulkan");
}
throw EXCEPTION("Invalid Frame Type");
@ -142,6 +144,7 @@ bool Rpcs3App::OnInit()
case rsx_renderer_type::OpenGL: return std::make_shared<GLGSRender>();
#ifdef _MSC_VER
case rsx_renderer_type::DX12: return std::make_shared<D3D12GSRender>();
case rsx_renderer_type::Vulkan: return std::make_shared<VKGSRender>();
#endif
default: throw EXCEPTION("Invalid GS Renderer %d", (int)mode);
}

View File

@ -94,12 +94,12 @@
<AdditionalIncludeDirectories>..\minidx9\Include;..\OpenAL\include;..\Vulkan\Vulkan-LoaderAndValidationLayers\include;..\Vulkan\glslang\glslang\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalLibraryDirectories Condition="'$(Configuration)|$(Platform)'=='Debug - MemLeak|x64'">..\Vulkan\Vulkan-build\loader\Debug;..\Vulkan\glslang-build\glslang\Debug;..\OpenAL\libs\Win64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\Vulkan\Vulkan-build\loader\Debug;..\Vulkan\glslang-build\glslang\Debug;..\OpenAL\libs\Win64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">..\Vulkan\Vulkan-build\loader\Debug;..\Vulkan\glslang-build\glslang\Debug;..\OpenAL\libs\Win64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\Vulkan\Vulkan-build\loader\Release;..\Vulkan\glslang-build\glslang\Release;..\OpenAL\libs\Win64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">..\Vulkan\Vulkan-build\loader\Release;..\Vulkan\glslang-build\glslang\Release;..\OpenAL\libs\Win64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>VKstatic.1.lib;glslang.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories Condition="'$(Configuration)|$(Platform)'=='Debug - MemLeak|x64'">..\Vulkan\glslang-build\SPIRV\Debug;..\Vulkan\glslang-build\OGLCompilersDLL\Debug;..\Vulkan\glslang-build\glslang\OSDependent\Windows\Debug;..\Vulkan\Vulkan-build\loader\Debug;..\Vulkan\glslang-build\glslang\Debug;..\OpenAL\libs\Win64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\Vulkan\glslang-build\SPIRV\Debug;..\Vulkan\glslang-build\OGLCompilersDLL\Debug;..\Vulkan\glslang-build\glslang\OSDependent\Windows\Debug;..\Vulkan\Vulkan-build\loader\Debug;..\Vulkan\glslang-build\glslang\Debug;..\OpenAL\libs\Win64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">..\Vulkan\glslang-build\SPIRV\Debug;..\Vulkan\glslang-build\OGLCompilersDLL\Debug;..\Vulkan\glslang-build\glslang\OSDependent\Windows\Debug;..\Vulkan\Vulkan-build\loader\Debug;..\Vulkan\glslang-build\glslang\Debug;..\OpenAL\libs\Win64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\Vulkan\glslang-build\SPIRV\Release;..\Vulkan\glslang-build\OGLCompilersDLL\Release;..\Vulkan\glslang-build\glslang\OSDependent\Windows\Release;..\Vulkan\Vulkan-build\loader\Release;..\Vulkan\glslang-build\glslang\Release;..\OpenAL\libs\Win64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">..\Vulkan\glslang-build\SPIRV\Release;..\Vulkan\glslang-build\OGLCompilersDLL\Release;..\Vulkan\glslang-build\glslang\OSDependent\Windows\Release;..\Vulkan\Vulkan-build\loader\Release;..\Vulkan\glslang-build\glslang\Release;..\OpenAL\libs\Win64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>VKstatic.1.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>