Merge pull request #8601 from hrydgard/vulkan

Vulkan rendering backend. Early Work-In-Progress
This commit is contained in:
Henrik Rydgård 2016-03-20 21:08:58 +01:00
commit 2b46ae59a0
130 changed files with 16810 additions and 526 deletions

View File

@ -6,6 +6,9 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/CMakeTests)
add_definitions(-DPPSSPP)
# None of these platforms support Vulkan yet.
add_definitions(-DNO_VULKAN)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(_ARCH_64 1)
add_definitions(-D_ARCH_64=1)

View File

@ -170,6 +170,7 @@
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StringPooling>true</StringPooling>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -239,6 +240,11 @@
<ClInclude Include="ThreadSafeList.h" />
<ClInclude Include="Thunk.h" />
<ClInclude Include="Timer.h" />
<ClInclude Include="Vulkan\SPIRVDisasm.h" />
<ClInclude Include="Vulkan\VulkanContext.h" />
<ClInclude Include="Vulkan\VulkanImage.h" />
<ClInclude Include="Vulkan\VulkanLoader.h" />
<ClInclude Include="Vulkan\VulkanMemory.h" />
<ClInclude Include="x64Analyzer.h" />
<ClInclude Include="x64Emitter.h" />
</ItemGroup>
@ -307,6 +313,11 @@
<ClCompile Include="ThreadPools.cpp" />
<ClCompile Include="Thunk.cpp" />
<ClCompile Include="Timer.cpp" />
<ClCompile Include="Vulkan\SPIRVDisasm.cpp" />
<ClCompile Include="Vulkan\VulkanContext.cpp" />
<ClCompile Include="Vulkan\VulkanImage.cpp" />
<ClCompile Include="Vulkan\VulkanLoader.cpp" />
<ClCompile Include="Vulkan\VulkanMemory.cpp" />
<ClCompile Include="x64Analyzer.cpp" />
<ClCompile Include="x64Emitter.cpp" />
</ItemGroup>

View File

@ -58,6 +58,21 @@
</ClInclude>
<ClInclude Include="GraphicsContext.h" />
<ClInclude Include="DbgNew.h" />
<ClInclude Include="Vulkan\SPIRVDisasm.h">
<Filter>Vulkan</Filter>
</ClInclude>
<ClInclude Include="Vulkan\VulkanLoader.h">
<Filter>Vulkan</Filter>
</ClInclude>
<ClInclude Include="Vulkan\VulkanContext.h">
<Filter>Vulkan</Filter>
</ClInclude>
<ClInclude Include="Vulkan\VulkanImage.h">
<Filter>Vulkan</Filter>
</ClInclude>
<ClInclude Include="Vulkan\VulkanMemory.h">
<Filter>Vulkan</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp" />
@ -103,6 +118,21 @@
<ClCompile Include="GL\GLInterface\GLInterface.cpp">
<Filter>GL\GLInterface</Filter>
</ClCompile>
<ClCompile Include="Vulkan\SPIRVDisasm.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\VulkanLoader.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\VulkanContext.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\VulkanImage.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\VulkanMemory.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />
@ -117,5 +147,8 @@
<Filter Include="GL\GLInterface">
<UniqueIdentifier>{2c723cf4-75b6-406a-90c0-ebb7a13ba476}</UniqueIdentifier>
</Filter>
<Filter Include="Vulkan">
<UniqueIdentifier>{c14d66ef-5f7c-4565-975a-72774e7ccfb9}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

View File

@ -22,6 +22,9 @@ public:
virtual void Resize() = 0;
// Needs casting to the appropriate type, unfortunately. Should find a better solution..
virtual void *GetAPIContext() { return nullptr; }
virtual Thin3DContext *CreateThin3DContext() = 0;
};

View File

@ -0,0 +1,616 @@
#include <sstream>
#include "Common/StringUtils.h"
#include "Common/Vulkan/SPIRVDisasm.h"
#include "ext/vulkan/spirv.hpp"
#define WRITE p+=sprintf
struct SpirvID {
std::string name;
int opcode;
};
struct OpInfo {
const char *name;
const char *extra;
};
static const char *executionModelNames[] = {
"Vertex", // 0,
"TessellationControl", // 1,
"TessellationEvaluation", // 2,
"Geometry", // 3,
"Fragment", // 4,
"GLCompute", // 5,
"Kernel", // 6,
};
static const char *storageClassNames[] = {
"UniformConstant",
"Input",
"Uniform",
"Output",
"Workgroup",
"CrossWorkgroup",
"Private",
"Function",
"Generic",
"PushConstant",
"AtomicCounter",
"Image",
};
static const char *decorationNames[] = {
"RelaxedPrecision", // 0,
"SpecId", // 1,
"Block", // 2,
"BufferBlock", // 3,
"RowMajor", // 4,
"ColMajor", // 5,
"ArrayStride", // 6,
"MatrixStride", // 7,
"GLSLShared", // 8,
"GLSLPacked", // 9,
"CPacked", // 10,
"BuiltIn", // 11,
nullptr, // 12
"NoPerspective", // 13,
"Flat", // 14,
"Patch", // 15,
"Centroid", // 16,
"Sample", // 17,
"Invariant", // 18,
"Restrict", // 19,
"Aliased", // 20,
"Volatile", // 21,
"Constant", // 22,
"Coherent", // 23,
"NonWritable", // 24,
"NonReadable", // 25,
"Uniform", // 26,
nullptr, // 27
"SaturatedConversion", // 28,
"Stream", // 29,
"Location", // 30,
"Component", // 31,
"Index", // 32,
"Binding", // 33,
"DescriptorSet", // 34,
"Offset", // 35,
"XfbBuffer", // 36,
"XfbStride", // 37,
"FuncParamAttr", // 38,
"FPRoundingMode", // 39,
"FPFastMathMode", // 40,
"LinkageAttributes", // 41,
"NoContraction", // 42,
"InputAttachmentIndex", // 43,
"Alignment", // 44,
};
static const OpInfo opInfo[] = {
{"Nop"}, // 0,
{"Undef"}, // 1,
{"SourceContinued"}, // 2,
{"Source"}, // 3,
{"SourceExtension"}, // 4,
{"Name"}, // 5,
{"MemberName"}, // 6,
{"String"}, // 7,
{"Line"}, // 8,
{nullptr}, // 9
{"Extension"}, // 10,
{"ExtInstImport"}, // 11,
{"ExtInst"}, // 12,
{nullptr}, // 13
{"MemoryModel"}, // 14,
{"EntryPoint"}, // 15,
{"ExecutionMode"}, // 16,
{"Capability"}, // 17,
{nullptr}, // 18
{"TypeVoid"}, // 19,
{"TypeBool"}, // 20,
{"TypeInt"}, // 21,
{"TypeFloat"}, // 22,
{"TypeVector"}, // 23,
{"TypeMatrix"}, // 24,
{"TypeImage"}, // 25,
{"TypeSampler"}, // 26,
{"TypeSampledImage"}, // 27,
{"TypeArray"}, // 28,
{"TypeRuntimeArray"}, // 29,
{"TypeStruct"}, // 30,
{"TypeOpaque"}, // 31,
{"TypePointer"}, // 32,
{"TypeFunction"}, // 33,
{"TypeEvent"}, // 34,
{"TypeDeviceEvent"}, // 35,
{"TypeReserveId"}, // 36,
{"TypeQueue"}, // 37,
{"TypePipe"}, // 38,
{"TypeForwardPointer"}, // 39,
{nullptr}, // 40
{"ConstantTrue"}, // 41,
{"ConstantFalse"}, // 42,
{"Constant"}, // 43,
{"ConstantComposite"}, // 44,
{"ConstantSampler"}, // 45,
{"ConstantNull"}, // 46,
{nullptr}, // 47
{"SpecConstantTrue"}, // 48,
{"SpecConstantFalse"}, // 49,
{"SpecConstant"}, // 50,
{"SpecConstantComposite"}, // 51,
{"SpecConstantOp"}, // 52,
{nullptr}, // 53
{"Function"}, // 54,
{"FunctionParameter"}, // 55,
{"FunctionEnd"}, // 56,
{"FunctionCall"}, // 57,
{nullptr}, // 58
{"Variable"}, // 59,
{"ImageTexelPointer"}, // 60,
{"Load"}, // 61,
{"Store"}, // 62,
{"CopyMemory"}, // 63,
{"CopyMemorySized"}, // 64,
{"AccessChain"}, // 65,
{"InBoundsAccessChain"}, // 66,
{"PtrAccessChain"}, // 67,
{"ArrayLength"}, // 68,
{"GenericPtrMemSemantics"}, // 69,
{"InBoundsPtrAccessChain"}, // 70,
{"Decorate"}, // 71,
{"MemberDecorate"}, // 72,
{"DecorationGroup"}, // 73,
{"GroupDecorate"}, // 74,
{"GroupMemberDecorate"}, // 75,
{nullptr}, // 76
{"VectorExtractDynamic"}, // 77,
{"VectorInsertDynamic"}, // 78,
{"VectorShuffle"}, // 79,
{"CompositeConstruct"}, // 80,
{"CompositeExtract"}, // 81,
{"CompositeInsert"}, // 82,
{"CopyObject"}, // 83,
{"Transpose"}, // 84,
{nullptr}, // 85
{"SampledImage"}, // 86,
{"ImageSampleImplicitLod"}, // 87,
{"ImageSampleExplicitLod"}, // 88,
{"ImageSampleDrefImplicitLod"}, // 89,
{"ImageSampleDrefExplicitLod"}, // 90,
{"ImageSampleProjImplicitLod"}, // 91,
{"ImageSampleProjExplicitLod"}, // 92,
{"ImageSampleProjDrefImplicitLod"}, // 93,
{"ImageSampleProjDrefExplicitLod"}, // 94,
{"ImageFetch"}, // 95,
{"ImageGather"}, // 96,
{"ImageDrefGather"}, // 97,
{"ImageRead"}, // 98,
{"ImageWrite"}, // 99,
{"Image"}, // 100,
{"ImageQueryFormat"}, // 101,
{"ImageQueryOrder"}, // 102,
{"ImageQuerySizeLod"}, // 103,
{"ImageQuerySize"}, // 104,
{"ImageQueryLod"}, // 105,
{"ImageQueryLevels"}, // 106,
{"ImageQuerySamples"}, // 107,
{nullptr}, // 108
{"ConvertFToU"}, // 109,
{"ConvertFToS"}, // 110,
{"ConvertSToF"}, // 111,
{"ConvertUToF"}, // 112,
{"UConvert"}, // 113,
{"SConvert"}, // 114,
{"FConvert"}, // 115,
{"QuantizeToF16"}, // 116,
{"ConvertPtrToU"}, // 117,
{"SatConvertSToU"}, // 118,
{"SatConvertUToS"}, // 119,
{"ConvertUToPtr"}, // 120,
{"PtrCastToGeneric"}, // 121,
{"GenericCastToPtr"}, // 122,
{"GenericCastToPtrExplicit"}, // 123,
{"Bitcast"}, // 124,
{nullptr}, // 125
{"SNegate"}, // 126,
{"FNegate"}, // 127,
{"IAdd"}, // 128,
{"FAdd", "+"}, // 129,
{"ISub"}, // 130,
{"FSub", "-"}, // 131,
{"IMul"}, // 132,
{"FMul", "*"}, // 133,
{"UDiv"}, // 134,
{"SDiv"}, // 135,
{"FDiv", "/"}, // 136,
{"UMod"}, // 137,
{"SRem"}, // 138,
{"SMod"}, // 139,
{"FRem"}, // 140,
{"FMod", "%"}, // 141,
{"VectorTimesScalar"}, // 142,
{"MatrixTimesScalar"}, // 143,
{"VectorTimesMatrix"}, // 144,
{"MatrixTimesVector"}, // 145,
{"MatrixTimesMatrix"}, // 146,
{"OuterProduct"}, // 147,
{"Dot", " dot "}, // 148,
{"IAddCarry"}, // 149,
{"ISubBorrow"}, // 150,
{"UMulExtended"}, // 151,
{"SMulExtended"}, // 152,
{nullptr}, // 153
{"Any"}, // 154,
{"All"}, // 155,
{"IsNan"}, // 156,
{"IsInf"}, // 157,
{"IsFinite"}, // 158,
{"IsNormal"}, // 159,
{"SignBitSet"}, // 160,
{"LessOrGreater"}, // 161,
{"Ordered"}, // 162,
{"Unordered"}, // 163,
{"LogicalEqual"}, // 164,
{"LogicalNotEqual"}, // 165,
{"LogicalOr"}, // 166,
{"LogicalAnd"}, // 167,
{"LogicalNot"}, // 168,
{"Select"}, // 169,
{"IEqual"}, // 170,
{"INotEqual"}, // 171,
{"UGreaterThan"}, // 172,
{"SGreaterThan"}, // 173,
{"UGreaterThanEqual"}, // 174,
{"SGreaterThanEqual"}, // 175,
{"ULessThan"}, // 176,
{"SLessThan"}, // 177,
{"ULessThanEqual"}, // 178,
{"SLessThanEqual"}, // 179,
{"FOrdEqual", "=="}, // 180,
{"FUnordEqual", "=="}, // 181,
{"FOrdNotEqual", "!="}, // 182,
{"FUnordNotEqual", "!="}, // 183,
{"FOrdLessThan"}, // 184,
{"FUnordLessThan"}, // 185,
{"FOrdGreaterThan"}, // 186,
{"FUnordGreaterThan"}, // 187,
{"FOrdLessThanEqual"}, // 188,
{"FUnordLessThanEqual"}, // 189,
{"FOrdGreaterThanEqual"}, // 190,
{"FUnordGreaterThanEqual"}, // 191,
{ nullptr }, // 192
{ nullptr }, // 193
{"ShiftRightLogical"}, // 194,
{"ShiftRightArithmetic"}, // 195,
{"ShiftLeftLogical"}, // 196,
{"BitwiseOr", "|"}, // 197,
{"BitwiseXor", "^"}, // 198,
{"BitwiseAnd", "&"}, // 199,
{"Not", "~"}, // 200,
{"BitFieldInsert"}, // 201,
{"BitFieldSExtract"}, // 202,
{"BitFieldUExtract"}, // 203,
{"BitReverse"}, // 204,
{"BitCount"}, // 205,
{nullptr},
{"DPdx"}, // 207,
{"DPdy"}, // 208,
{"Fwidth"}, // 209,
{"DPdxFine"}, // 210,
{"DPdyFine"}, // 211,
{"FwidthFine"}, // 212,
{"DPdxCoarse"}, // 213,
{"DPdyCoarse"}, // 214,
{"FwidthCoarse"}, // 215,
{nullptr}, // 216
{nullptr}, // 217
{"EmitVertex"}, // 218,
{"EndPrimitive"}, // 219,
{"EmitStreamVertex"}, // 220,
{"EndStreamPrimitive"}, // 221,
{nullptr}, // 222
{nullptr}, // 223
{"ControlBarrier"}, // 224,
{"MemoryBarrier"}, // 225,
{nullptr}, // 226
{"AtomicLoad"}, // 227,
{"AtomicStore"}, // 228,
{"AtomicExchange"}, // 229,
{"AtomicCompareExchange"}, // 230,
{"AtomicCompareExchangeWeak"}, // 231,
{"AtomicIIncrement"}, // 232,
{"AtomicIDecrement"}, // 233,
{"AtomicIAdd"}, // 234,
{"AtomicISub"}, // 235,
{"AtomicSMin"}, // 236,
{"AtomicUMin"}, // 237,
{"AtomicSMax"}, // 238,
{"AtomicUMax"}, // 239,
{"AtomicAnd"}, // 240,
{"AtomicOr"}, // 241,
{"AtomicXor"}, // 242,
{ nullptr }, // 243,
{ nullptr }, // 244,
{"Phi"}, // 245,
{"LoopMerge"}, // 246,
{"SelectionMerge"}, // 247,
{"Label"}, // 248,
{"Branch"}, // 249,
{"BranchConditional"}, // 250,
{"Switch"}, // 251,
{"Kill"}, // 252,
{"Return"}, // 253,
{"ReturnValue"}, // 254,
{"Unreachable"}, // 255,
{"LifetimeStart"}, // 256,
{"LifetimeStop"}, // 257,
{ nullptr },
{"GroupAsyncCopy"}, // 259,
{"GroupWaitEvents"}, // 260,
{"GroupAll"}, // 261,
{"GroupAny"}, // 262,
{"GroupBroadcast"}, // 263,
{"GroupIAdd"}, // 264,
{"GroupFAdd"}, // 265,
{"GroupFMin"}, // 266,
{"GroupUMin"}, // 267,
{"GroupSMin"}, // 268,
{"GroupFMax"}, // 269,
{"GroupUMax"}, // 270,
{"GroupSMax"}, // 271,
{nullptr}, // 272
{nullptr}, // 273
{"ReadPipe"}, // 274,
{"WritePipe"}, // 275,
{"ReservedReadPipe"}, // 276,
{"ReservedWritePipe"}, // 277,
{"ReserveReadPipePackets"}, // 278,
{"ReserveWritePipePackets"}, // 279,
{"CommitReadPipe"}, // 280,
{"CommitWritePipe"}, // 281,
{"IsValidReserveId"}, // 282,
{"GetNumPipePackets"}, // 283,
{"GetMaxPipePackets"}, // 284,
{"GroupReserveReadPipePackets"}, // 285,
{"GroupReserveWritePipePackets"}, // 286,
{"GroupCommitReadPipe"}, // 287,
{"GroupCommitWritePipe"}, // 288,
{ nullptr }, // 289
{ nullptr }, // 290
{"EnqueueMarker"}, // 291,
{"EnqueueKernel"}, // 292,
{"GetKernelNDrangeSubGroupCount"}, // 293,
{"GetKernelNDrangeMaxSubGroupSize"}, // 294,
{"GetKernelWorkGroupSize"}, // 295,
{"GetKernelPreferredWorkGroupSizeMultiple"}, // 296,
{"RetainEvent"}, // 297,
{"ReleaseEvent"}, // 298,
{"CreateUserEvent"}, // 299,
{"IsValidEvent"}, // 300,
{"SetUserEventStatus"}, // 301,
{"CaptureEventProfilingInfo"}, // 302,
{"GetDefaultQueue"}, // 303,
{"BuildNDRange"}, // 304,
{"ImageSparseSampleImplicitLod"}, // 305,
{"ImageSparseSampleExplicitLod"}, // 306,
{"ImageSparseSampleDrefImplicitLod"}, // 307,
{"ImageSparseSampleDrefExplicitLod"}, // 308,
{"ImageSparseSampleProjImplicitLod"}, // 309,
{"ImageSparseSampleProjExplicitLod"}, // 310,
{"ImageSparseSampleProjDrefImplicitLod"}, // 311,
{"ImageSparseSampleProjDrefExplicitLod"}, // 312,
{"ImageSparseFetch"}, // 313,
{"ImageSparseGather"}, // 314,
{"ImageSparseDrefGather"}, // 315,
{"ImageSparseTexelsResident"}, // 316,
{"NoLine"}, // 317,
{"AtomicFlagTestAndSet"}, // 318,
{"AtomicFlagClear"}, // 319,
};
static std::string ReadSpirvString(const std::vector<uint32_t> &spirv, int offset, int *outOffset = nullptr) {
bool done = false;
std::string temp;
while (!done) {
uint32_t data = spirv[offset++];
for (int i = 0; i < 4; i++) {
char c = (char)(data & 0xff);
if (!c) {
done = true;
break;
}
temp.push_back(data);
data >>= 8;
}
if (offset == (int)spirv.size())
break;
}
if (outOffset)
*outOffset = offset;
return temp;
}
bool DisassembleSPIRV(std::vector<uint32_t> spirv, std::string *output) {
if (spirv.size() < 10) {
*output = "Too small";
return false;
}
uint32_t magic = spirv[0];
uint32_t version = spirv[1];
uint32_t generator = spirv[2];
int bound = (int)spirv[3]; // Max ID used in file.
// spirv[4] is reserved for schema.
std::vector<SpirvID> ids;
ids.resize(bound);
for (int i = 0; i < bound; i++) {
ids[i].name = StringFromFormat("%%%d", i);
}
if (magic != 0x07230203) {
*output = "Not SPIRV";
return false;
}
int indent = 0;
char *buffer = new char[1024 * 1024];
char *p = buffer;
WRITE(p, "// ======= SPIR-V version %08x =======\n// Max ID: %d\n", version, bound); // GLSL ES
int i = 5;
while (i < (int)spirv.size()) {
uint32_t d = spirv[i];
int wordCount = d >> 16;
int opcode = d & 0xFFFF;
const OpInfo &op = opInfo[opcode];
int target = (i < (int)spirv.size() - 1) ? spirv[(i + 1)] : 0; // Not always, but used often enough that we get it here.
int source, source2, resType;
std::string name;
switch (opcode) {
case spv::OpTypeVoid:
case spv::OpTypeBool:
case spv::OpTypeInt:
case spv::OpTypeFloat:
case spv::OpTypeMatrix:
case spv::OpTypeImage:
case spv::OpTypeSampler:
case spv::OpTypeSampledImage:
case spv::OpTypeRuntimeArray:
case spv::OpTypeOpaque:
case spv::OpTypePointer:
case spv::OpTypeFunction:
case spv::OpTypeEvent:
case spv::OpTypeDeviceEvent:
case spv::OpTypeReserveId:
case spv::OpTypeQueue:
case spv::OpTypePipe:
case spv::OpTypeForwardPointer:
ids[target].name = op.name + 4; // Remove "Type"
ids[target].opcode = opcode;
break;
case spv::OpTypeVector:
source = spirv[i + 2];
source2 = spirv[i + 3];
ids[target].name = ids[source].name + StringFromFormat("%d", source2); break;
break;
case spv::OpTypeArray:
source = spirv[i + 2];
source2 = spirv[i + 3];
ids[target].name = ids[source].name + "[" + ids[source2].name + "]"; break;
break;
case spv::OpTypeStruct:
{
ids[target].name = "struct" + ids[target].name;
WRITE(p, "struct {\n");
if (wordCount == 3) {
source = spirv[i + 2];
WRITE(p, " %s\n", ids[source].name.c_str());
} else {
int numMembers = (wordCount - 2) / 2;
for (int m = 0; m < numMembers; m++) {
int id = spirv[i + 2 + m];
int type = spirv[i + 2 + m + numMembers];
WRITE(p, " %s %s;\n", ids[type].name.c_str(), ids[id].name.c_str());
}
}
WRITE(p, "};\n");
}
break;
case spv::OpVariable:
resType = spirv[i + 1];
target = spirv[i + 2];
source = spirv[i + 3];
source2 = spirv[i + 4];
ids[target].name = storageClassNames[source];
break;
case spv::OpDecorate:
source = spirv[i + 2];
ids[target].name += std::string("[") + decorationNames[source] + "]";
break;
case spv::OpName:
ids[target].name = ReadSpirvString(spirv, i + 2);
// WRITE(p, "Name %d: '%s'\n", target, ids[target].name.c_str());
break;
case spv::OpMemberName:
break;
case spv::OpStore:
source = spirv[i + 2];
WRITE(p, "Store(%s, %s)\n", ids[target].name.c_str(), ids[source].name.c_str());
break;
case spv::OpLoad:
resType = spirv[i + 1];
target = spirv[i + 2];
source = spirv[i + 3];
WRITE(p, "%s (%s) := Load(%s)\n", ids[target].name.c_str(), ids[resType].name.c_str(), ids[source].name.c_str());
break;
case spv::OpFAdd:
case spv::OpFMul:
case spv::OpFDiv:
case spv::OpFSub:
resType = spirv[i + 2];
source = spirv[i + 3];
source2 = spirv[i + 4];
WRITE(p, "%s (%s) := %s %s %s\n", ids[target].name.c_str(), ids[resType].name.c_str(), ids[source].name.c_str(), op.extra, ids[source2].name.c_str());
break;
case spv::OpCapability:
case spv::OpMemoryModel:
case spv::OpExtInstImport:
case spv::OpExecutionMode:
case spv::OpGroupAsyncCopy:
case spv::OpSource:
case spv::OpExtension:
// hide these for now
break;
case spv::OpEntryPoint:
source = spirv[i + 1];
source2 = spirv[i + 2];
name = ReadSpirvString(spirv, i + 3);
WRITE(p, "EntryPoint %s: '%s' %s : %s\n", executionModelNames[target], ids[source].name.c_str(), ids[source2].name.c_str(), name.c_str());
break;
case spv::OpFunction:
resType = spirv[i + 1];
target = spirv[i + 2];
source = spirv[i + 3];
ids[target].name = ids[resType].name + "()";
WRITE(p, "Function %s {\n", ids[target].name.c_str());
break;
case spv::OpLabel:
WRITE(p, "label %s:\n", ids[target].name.c_str());
break;
case spv::OpReturnValue:
WRITE(p, "return %s\n", ids[target].name.c_str());
break;
case spv::OpReturn:
WRITE(p, "return\n");
break;
case spv::OpFunctionEnd:
WRITE(p, "}\n");
break;
case spv::OpSourceExtension:
break;
default:
WRITE(p, "%s (%d data words)\n", op.name, wordCount - 1);
break;
}
i += wordCount;
}
*output = buffer;
delete[] buffer;
return true;
}

View File

@ -0,0 +1,10 @@
#pragma once
#include <cstdint>
#include <string>
#include <vector>
// Mostly just a toy to understand SPIR-V better. Not an accurate SPIR-V disassembler,
// and the result will not reassemble.
bool DisassembleSPIRV(std::vector<uint32_t> spirv, std::string *output);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,346 @@
#ifndef UTIL_INIT
#define UTIL_INIT
#ifdef ANDROID
#undef NDEBUG // asserts
#endif
#include <cassert>
#include <string>
#include <vector>
#include <utility>
#include "base/logging.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define VK_USE_PLATFORM_WIN32_KHR
#define NOMINMAX /* Don't let Windows define min() or max() */
#define APP_NAME_STR_LEN 80
#include <Windows.h>
#elif defined(ANDROID) // _WIN32
#include <android/native_window_jni.h>
#define VK_USE_PLATFORM_ANDROID_KHR
#else
#define VK_USE_PLATFORM_XCB_KHR
#include <unistd.h>
#endif // _WIN32
#include "Common/Vulkan/VulkanLoader.h"
// Amount of time, in nanoseconds, to wait for a command buffer to complete
#define FENCE_TIMEOUT 10000000000
#if defined(NDEBUG) && defined(__GNUC__)
#define U_ASSERT_ONLY __attribute__((unused))
#else
#define U_ASSERT_ONLY
#endif
enum {
VULKAN_FLAG_VALIDATE = 1,
VULKAN_FLAG_PRESENT_MAILBOX = 2,
VULKAN_FLAG_PRESENT_IMMEDIATE = 4,
VULKAN_FLAG_PRESENT_FIFO_RELAXED = 8,
};
// A layer can expose extensions, keep track of those extensions here.
struct layer_properties {
VkLayerProperties properties;
std::vector<VkExtensionProperties> extensions;
};
// This is a bit repetitive...
class VulkanDeleteList {
public:
void QueueDeleteDescriptorPool(VkDescriptorPool pool) { descPools_.push_back(pool); }
void QueueDeleteShaderModule(VkShaderModule module) { modules_.push_back(module); }
void QueueDeleteBuffer(VkBuffer buffer) { buffers_.push_back(buffer); }
void QueueDeleteBufferView(VkBufferView bufferView) { bufferViews_.push_back(bufferView); }
void QueueDeleteImage(VkImage image) { images_.push_back(image); }
void QueueDeleteImageView(VkImageView imageView) { imageViews_.push_back(imageView); }
void QueueDeleteDeviceMemory(VkDeviceMemory deviceMemory) { deviceMemory_.push_back(deviceMemory); }
void QueueDeleteSampler(VkSampler sampler) { samplers_.push_back(sampler); }
void QueueDeletePipelineCache(VkPipelineCache pipelineCache) { pipelineCaches_.push_back(pipelineCache); }
void Take(VulkanDeleteList &del) {
assert(descPools_.size() == 0);
assert(modules_.size() == 0);
assert(buffers_.size() == 0);
assert(bufferViews_.size() == 0);
assert(images_.size() == 0);
assert(imageViews_.size() == 0);
assert(deviceMemory_.size() == 0);
assert(samplers_.size() == 0);
assert(pipelineCaches_.size() == 0);
descPools_ = std::move(del.descPools_);
modules_ = std::move(del.modules_);
buffers_ = std::move(del.buffers_);
bufferViews_ = std::move(del.bufferViews_);
images_ = std::move(del.images_);
imageViews_ = std::move(del.imageViews_);
deviceMemory_ = std::move(del.deviceMemory_);
samplers_ = std::move(del.samplers_);
pipelineCaches_ = std::move(del.pipelineCaches_);
}
void PerformDeletes(VkDevice device) {
for (auto &descPool : descPools_) {
vkDestroyDescriptorPool(device, descPool, nullptr);
}
descPools_.clear();
for (auto &module : modules_) {
vkDestroyShaderModule(device, module, nullptr);
}
modules_.clear();
for (auto &buf : buffers_) {
vkDestroyBuffer(device, buf, nullptr);
}
buffers_.clear();
for (auto &bufView : bufferViews_) {
vkDestroyBufferView(device, bufView, nullptr);
}
bufferViews_.clear();
for (auto &image : images_) {
vkDestroyImage(device, image, nullptr);
}
images_.clear();
for (auto &imageView : imageViews_) {
vkDestroyImageView(device, imageView, nullptr);
}
imageViews_.clear();
for (auto &mem : deviceMemory_) {
vkFreeMemory(device, mem, nullptr);
}
deviceMemory_.clear();
for (auto &sampler : samplers_) {
vkDestroySampler(device, sampler, nullptr);
}
samplers_.clear();
for (auto &pcache : pipelineCaches_) {
vkDestroyPipelineCache(device, pcache, nullptr);
}
pipelineCaches_.clear();
}
private:
std::vector<VkDescriptorPool> descPools_;
std::vector<VkShaderModule> modules_;
std::vector<VkBuffer> buffers_;
std::vector<VkBufferView> bufferViews_;
std::vector<VkImage> images_;
std::vector<VkImageView> imageViews_;
std::vector<VkDeviceMemory> deviceMemory_;
std::vector<VkSampler> samplers_;
std::vector<VkPipelineCache> pipelineCaches_;
};
// VulkanContext sets up the basics necessary for rendering to a window, including framebuffers etc.
// Optionally, it can create a depth buffer for you as well.
class VulkanContext {
public:
VulkanContext(const char *app_name, int app_ver, uint32_t flags);
~VulkanContext();
VkResult CreateDevice(int physical_device);
const std::string &InitError() { return init_error_; }
VkDevice GetDevice() { return device_; }
VkInstance GetInstance() { return instance_; }
VulkanDeleteList &Delete() { return globalDeleteList_; }
VkPipelineCache CreatePipelineCache();
#ifdef _WIN32
void InitSurfaceWin32(HINSTANCE conn, HWND wnd);
#elif ANDROID
void InitSurfaceAndroid(ANativeWindow *native_window, int width, int height);
#endif
void InitQueue();
void InitObjects(bool depthPresent);
void InitSwapchain(VkCommandBuffer cmd);
void InitSurfaceRenderPass(bool include_depth, bool clear);
void InitFramebuffers(bool include_depth);
void InitDepthStencilBuffer(VkCommandBuffer cmd);
void InitCommandPool();
void DestroyObjects();
void DestroySurfaceRenderPass();
void DestroyFramebuffers();
void DestroySwapChain();
void DestroyDepthStencilBuffer();
void DestroyCommandPool();
void DestroyDevice();
void WaitUntilQueueIdle();
// Utility functions for shorter code
VkFence CreateFence(bool presignalled);
bool CreateShaderModule(const std::vector<uint32_t> &spirv, VkShaderModule *shaderModule);
void WaitAndResetFence(VkFence fence);
int GetWidth() { return width; }
int GetHeight() { return height; }
VkCommandBuffer GetInitCommandBuffer();
// This must only be accessed between BeginSurfaceRenderPass and EndSurfaceRenderPass.
VkCommandBuffer GetSurfaceCommandBuffer() {
return frame_[curFrame_ & 1].cmdBuf;
}
// The surface render pass is special because it has to acquire the backbuffer, and may thus "block".
// Use the returned command buffer to enqueue commands that render to the backbuffer.
// To render to other buffers first, you can submit additional commandbuffers using QueueBeforeSurfaceRender(cmd).
VkCommandBuffer BeginSurfaceRenderPass(VkClearValue clear_values[2]);
// May eventually need the ability to break and resume the backbuffer render pass in a few rare cases.
void EndSurfaceRenderPass();
void QueueBeforeSurfaceRender(VkCommandBuffer cmd);
bool MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex);
VkResult InitDebugMsgCallback(PFN_vkDebugReportCallbackEXT dbgFunc, int bits, void *userdata = nullptr);
void DestroyDebugMsgCallback();
VkRenderPass GetSurfaceRenderPass() const {
return surface_render_pass_;
}
VkPhysicalDevice GetPhysicalDevice(int n = 0) const {
return physical_devices_[n];
}
VkQueue GetGraphicsQueue() const {
return gfx_queue_;
}
int GetGraphicsQueueFamilyIndex() const {
return graphics_queue_family_index_;
}
const VkPhysicalDeviceProperties &GetPhysicalDeviceProperties() {
return gpu_props;
}
VkResult InitGlobalExtensionProperties();
VkResult InitLayerExtensionProperties(layer_properties &layer_props);
VkResult InitGlobalLayerProperties();
VkResult InitDeviceExtensionProperties(layer_properties &layer_props);
VkResult InitDeviceLayerProperties();
const VkPhysicalDeviceFeatures &GetFeaturesAvailable() const { return featuresAvailable_; }
const VkPhysicalDeviceFeatures &GetFeaturesEnabled() const { return featuresEnabled_; }
private:
VkSemaphore acquireSemaphore;
#ifdef _WIN32
HINSTANCE connection; // hInstance - Windows Instance
HWND window; // hWnd - window handle
#elif ANDROID // _WIN32
ANativeWindow *native_window;
#endif // _WIN32
// TODO: Move to frame data
VkCommandPool cmd_pool_;
VkInstance instance_;
VkDevice device_;
VkQueue gfx_queue_;
VkSurfaceKHR surface_;
bool prepared;
bool use_staging_buffer_;
std::string init_error_;
std::vector<const char *> instance_layer_names;
std::vector<const char *> instance_extension_names;
std::vector<layer_properties> instance_layer_properties;
std::vector<VkExtensionProperties> instance_extension_properties;
std::vector<const char *> device_layer_names;
std::vector<const char *> device_extension_names;
std::vector<layer_properties> device_layer_properties;
std::vector<VkExtensionProperties> device_extension_properties;
std::vector<VkPhysicalDevice> physical_devices_;
uint32_t graphics_queue_family_index_;
VkPhysicalDeviceProperties gpu_props;
std::vector<VkQueueFamilyProperties> queue_props;
VkPhysicalDeviceMemoryProperties memory_properties;
struct swap_chain_buffer {
VkImage image;
VkImageView view;
};
// Swap chain
int width, height;
int flags_;
VkFormat swapchain_format;
std::vector<VkFramebuffer> framebuffers_;
uint32_t swapchainImageCount;
VkSwapchainKHR swap_chain_;
std::vector<swap_chain_buffer> swapChainBuffers;
// Manages flipping command buffers for the backbuffer render pass.
// It is recommended to do the same for other rendering passes.
struct FrameData {
FrameData() : hasInitCommands(false), cmdInit(nullptr), cmdBuf(nullptr) {}
VkFence fence;
bool hasInitCommands;
VkCommandBuffer cmdInit;
VkCommandBuffer cmdBuf;
VulkanDeleteList deleteList;
};
FrameData frame_[2];
int curFrame_;
// At the end of the frame, this is copied into the frame's delete list, so it can be processed
// the next time the frame comes around again.
VulkanDeleteList globalDeleteList_;
std::vector<VkDebugReportCallbackEXT> msg_callbacks;
struct {
VkFormat format;
VkImage image;
VkDeviceMemory mem;
VkImageView view;
} depth;
VkRenderPass surface_render_pass_;
uint32_t current_buffer;
uint32_t queue_count;
VkPhysicalDeviceFeatures featuresAvailable_;
VkPhysicalDeviceFeatures featuresEnabled_;
std::vector<VkCommandBuffer> cmdQueue_;
};
// Stand-alone utility functions
void VulkanBeginCommandBuffer(VkCommandBuffer cmd);
void TransitionImageLayout(
VkCommandBuffer cmd,
VkImage image,
VkImageAspectFlags aspectMask,
VkImageLayout old_image_layout,
VkImageLayout new_image_layout);
// GLSL compiler
void init_glslang();
void finalize_glslang();
bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *pshader, std::vector<uint32_t> &spirv, std::string *errorMessage = nullptr);
#endif // UTIL_INIT

View File

@ -0,0 +1,356 @@
#include "Common/Vulkan/VulkanImage.h"
VkResult VulkanTexture::Create(int w, int h, VkFormat format) {
tex_width = w;
tex_height = h;
format_ = format;
VkFormatProperties formatProps;
vkGetPhysicalDeviceFormatProperties(vulkan_->GetPhysicalDevice(), format, &formatProps);
// See if we can use a linear tiled image for a texture, if not, we will need a staging image for the texture data.
// Linear tiling is usually only supported for 2D non-array textures.
// needStaging = (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) ? true : false;
// Always stage.
needStaging = true;
return VK_SUCCESS;
}
void VulkanTexture::CreateMappableImage() {
// If we already have a mappableImage, forget it.
if (mappableImage) {
vulkan_->Delete().QueueDeleteImage(mappableImage);
mappableImage = VK_NULL_HANDLE;
}
if (mappableMemory) {
vulkan_->Delete().QueueDeleteDeviceMemory(mappableMemory);
mappableMemory = VK_NULL_HANDLE;
}
bool U_ASSERT_ONLY pass;
VkImageCreateInfo image_create_info = {};
image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_create_info.pNext = NULL;
image_create_info.imageType = VK_IMAGE_TYPE_2D;
image_create_info.format = format_;
image_create_info.extent.width = tex_width;
image_create_info.extent.height = tex_height;
image_create_info.extent.depth = 1;
image_create_info.mipLevels = 1;
image_create_info.arrayLayers = 1;
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_create_info.tiling = VK_IMAGE_TILING_LINEAR;
image_create_info.usage = needStaging ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : VK_IMAGE_USAGE_SAMPLED_BIT;
image_create_info.queueFamilyIndexCount = 0;
image_create_info.pQueueFamilyIndices = NULL;
image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
image_create_info.flags = 0;
image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
VkMemoryAllocateInfo mem_alloc = {};
mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
mem_alloc.pNext = NULL;
mem_alloc.allocationSize = 0;
mem_alloc.memoryTypeIndex = 0;
// Create a mappable image. It will be the texture if linear images are ok to be textures
// or it will be the staging image if they are not.
VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &mappableImage);
assert(res == VK_SUCCESS);
vkGetImageMemoryRequirements(vulkan_->GetDevice(), mappableImage, &mem_reqs);
assert(res == VK_SUCCESS);
mem_alloc.allocationSize = mem_reqs.size;
// Find the memory type that is host mappable.
pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &mem_alloc.memoryTypeIndex);
assert(pass);
res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mappableMemory);
assert(res == VK_SUCCESS);
res = vkBindImageMemory(vulkan_->GetDevice(), mappableImage, mappableMemory, 0);
assert(res == VK_SUCCESS);
}
uint8_t *VulkanTexture::Lock(int level, int *rowPitch) {
CreateMappableImage();
VkImageSubresource subres = {};
subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subres.mipLevel = 0;
subres.arrayLayer = 0;
VkSubresourceLayout layout;
void *data;
// Get the subresource layout so we know what the row pitch is
vkGetImageSubresourceLayout(vulkan_->GetDevice(), mappableImage, &subres, &layout);
VkResult res = vkMapMemory(vulkan_->GetDevice(), mappableMemory, layout.offset, layout.size, 0, &data);
assert(res == VK_SUCCESS);
*rowPitch = (int)layout.rowPitch;
return (uint8_t *)data;
}
void VulkanTexture::Unlock() {
vkUnmapMemory(vulkan_->GetDevice(), mappableMemory);
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
// if we already have an image, queue it for destruction and forget it.
Wipe();
if (!needStaging) {
// If we can use the linear tiled image as a texture, just do it
image = mappableImage;
mem = mappableMemory;
TransitionImageLayout(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
// Make sure we don't accidentally delete the main image.
mappableImage = VK_NULL_HANDLE;
mappableMemory = VK_NULL_HANDLE;
} else {
VkImageCreateInfo image_create_info = {};
image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_create_info.pNext = NULL;
image_create_info.imageType = VK_IMAGE_TYPE_2D;
image_create_info.format = format_;
image_create_info.extent.width = tex_width;
image_create_info.extent.height = tex_height;
image_create_info.extent.depth = 1;
image_create_info.mipLevels = 1;
image_create_info.arrayLayers = 1;
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_create_info.queueFamilyIndexCount = 0;
image_create_info.pQueueFamilyIndices = NULL;
image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
image_create_info.flags = 0;
// The mappable image cannot be our texture, so create an optimally tiled image and blit to it
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &image);
assert(res == VK_SUCCESS);
vkGetImageMemoryRequirements(vulkan_->GetDevice(), image, &mem_reqs);
VkMemoryAllocateInfo mem_alloc = {};
mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
mem_alloc.pNext = NULL;
mem_alloc.memoryTypeIndex = 0;
mem_alloc.allocationSize = mem_reqs.size;
// Find memory type - don't specify any mapping requirements
bool pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &mem_alloc.memoryTypeIndex);
assert(pass);
res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mem);
assert(res == VK_SUCCESS);
res = vkBindImageMemory(vulkan_->GetDevice(), image, mem, 0);
assert(res == VK_SUCCESS);
// Since we're going to blit from the mappable image, set its layout to SOURCE_OPTIMAL
TransitionImageLayout(cmd, mappableImage,
VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_PREINITIALIZED,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
TransitionImageLayout(cmd, image,
VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
VkImageCopy copy_region;
copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy_region.srcSubresource.mipLevel = 0;
copy_region.srcSubresource.baseArrayLayer = 0;
copy_region.srcSubresource.layerCount = 1;
copy_region.srcOffset.x = 0;
copy_region.srcOffset.y = 0;
copy_region.srcOffset.z = 0;
copy_region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy_region.dstSubresource.mipLevel = 0;
copy_region.dstSubresource.baseArrayLayer = 0;
copy_region.dstSubresource.layerCount = 1;
copy_region.dstOffset.x = 0;
copy_region.dstOffset.y = 0;
copy_region.dstOffset.z = 0;
copy_region.extent.width = tex_width;
copy_region.extent.height = tex_height;
copy_region.extent.depth = 1;
// Put the copy command into the command buffer
vkCmdCopyImage(cmd,
mappableImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &copy_region);
assert(res == VK_SUCCESS);
// Set the layout for the texture image from DESTINATION_OPTIMAL to SHADER_READ_ONLY
TransitionImageLayout(cmd, image,
VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
// Then drop the temporary mappable image - although should not be necessary...
vulkan_->Delete().QueueDeleteImage(mappableImage);
vulkan_->Delete().QueueDeleteDeviceMemory(mappableMemory);
mappableImage = VK_NULL_HANDLE;
mappableMemory = VK_NULL_HANDLE;
}
VkImageViewCreateInfo view_info = {};
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
view_info.pNext = NULL;
view_info.image = VK_NULL_HANDLE;
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
view_info.format = format_;
view_info.components.r = VK_COMPONENT_SWIZZLE_R;
view_info.components.g = VK_COMPONENT_SWIZZLE_G;
view_info.components.b = VK_COMPONENT_SWIZZLE_B;
view_info.components.a = VK_COMPONENT_SWIZZLE_A;
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
view_info.subresourceRange.baseMipLevel = 0;
view_info.subresourceRange.levelCount = 1;
view_info.subresourceRange.baseArrayLayer = 0;
view_info.subresourceRange.layerCount = 1;
view_info.image = image;
VkResult res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &view);
assert(res == VK_SUCCESS);
}
void VulkanTexture::Wipe() {
if (image) {
vulkan_->Delete().QueueDeleteImage(image);
image = VK_NULL_HANDLE;
}
if (view) {
vulkan_->Delete().QueueDeleteImageView(view);
view = VK_NULL_HANDLE;
}
if (mem) {
vulkan_->Delete().QueueDeleteDeviceMemory(mem);
mem = VK_NULL_HANDLE;
}
}
void VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkImageUsageFlags usage) {
Wipe();
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
tex_width = w;
tex_height = h;
numMips_ = numMips;
format_ = format;
VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
image_create_info.imageType = VK_IMAGE_TYPE_2D;
image_create_info.format = format_;
image_create_info.extent.width = tex_width;
image_create_info.extent.height = tex_height;
image_create_info.extent.depth = 1;
image_create_info.mipLevels = numMips;
image_create_info.arrayLayers = 1;
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_create_info.flags = 0;
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &image);
assert(res == VK_SUCCESS);
vkGetImageMemoryRequirements(vulkan_->GetDevice(), image, &mem_reqs);
VkMemoryAllocateInfo mem_alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
mem_alloc.memoryTypeIndex = 0;
mem_alloc.allocationSize = mem_reqs.size;
// Find memory type - don't specify any mapping requirements
bool pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &mem_alloc.memoryTypeIndex);
assert(pass);
res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mem);
assert(res == VK_SUCCESS);
res = vkBindImageMemory(vulkan_->GetDevice(), image, mem, 0);
assert(res == VK_SUCCESS);
// Since we're going to blit to the target, set its layout to TRANSFER_DST
TransitionImageLayout(cmd, image,
VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
// Create the view while we're at it.
VkImageViewCreateInfo view_info = {};
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
view_info.pNext = NULL;
view_info.image = VK_NULL_HANDLE;
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
view_info.format = format_;
view_info.components.r = VK_COMPONENT_SWIZZLE_R;
view_info.components.g = VK_COMPONENT_SWIZZLE_G;
view_info.components.b = VK_COMPONENT_SWIZZLE_B;
view_info.components.a = VK_COMPONENT_SWIZZLE_A;
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
view_info.subresourceRange.baseMipLevel = 0;
view_info.subresourceRange.levelCount = numMips;
view_info.subresourceRange.baseArrayLayer = 0;
view_info.subresourceRange.layerCount = 1;
view_info.image = image;
res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &view);
assert(res == VK_SUCCESS);
}
void VulkanTexture::UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength) {
VkBufferImageCopy copy_region = {};
copy_region.bufferOffset = offset;
copy_region.bufferRowLength = (uint32_t)rowLength;
copy_region.bufferImageHeight = 0; // 2D
copy_region.imageExtent.width = mipWidth;
copy_region.imageExtent.height = mipHeight;
copy_region.imageExtent.depth = 1;
copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy_region.imageSubresource.mipLevel = mip;
copy_region.imageSubresource.baseArrayLayer = 0;
copy_region.imageSubresource.layerCount = 1;
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
vkCmdCopyBufferToImage(cmd, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy_region);
}
void VulkanTexture::EndCreate() {
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
TransitionImageLayout(cmd, image,
VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
void VulkanTexture::Destroy() {
if (view) {
vulkan_->Delete().QueueDeleteImageView(view);
}
if (image) {
vulkan_->Delete().QueueDeleteImage(image);
if (mappableImage == image) {
mappableImage = VK_NULL_HANDLE;
}
}
if (mem) {
vulkan_->Delete().QueueDeleteDeviceMemory(mem);
if (mappableMemory == mem) {
mappableMemory = VK_NULL_HANDLE;
}
}
view = VK_NULL_HANDLE;
image = VK_NULL_HANDLE;
mem = VK_NULL_HANDLE;
}

View File

@ -0,0 +1,50 @@
#pragma once
#include "Common/Vulkan/VulkanContext.h"
// Wrapper around what you need to use a texture.
// Not very optimal - if you have many small textures you should use other strategies.
class VulkanTexture {
public:
VulkanTexture(VulkanContext *vulkan)
: vulkan_(vulkan), image(VK_NULL_HANDLE), mem(VK_NULL_HANDLE), view(VK_NULL_HANDLE),
tex_width(0), tex_height(0), numMips_(1), format_(VK_FORMAT_UNDEFINED),
mappableImage(VK_NULL_HANDLE), mappableMemory(VK_NULL_HANDLE), needStaging(false) {
memset(&mem_reqs, 0, sizeof(mem_reqs));
}
~VulkanTexture() {
Destroy();
}
// Simple usage - no cleverness, no mipmaps.
// Always call Create, Lock, Unlock. Unlock performs the upload if necessary.
// Can later Lock and Unlock again. This cannot change the format. Create cannot
// be called a second time without recreating the texture object until Destroy has
// been called.
VkResult Create(int w, int h, VkFormat format);
uint8_t *Lock(int level, int *rowPitch);
void Unlock();
// Fast uploads from buffer. Mipmaps supported. Usage must at least include VK_IMAGE_USAGE_TRANSFER_DST_BIT in order to use UploadMip.
void CreateDirect(int w, int h, int numMips, VkFormat format, VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
void UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels
void EndCreate();
int GetNumMips() const { return numMips_; }
void Destroy();
VkImageView GetImageView() const { return view; }
private:
void CreateMappableImage();
void Wipe();
VulkanContext *vulkan_;
VkImage image;
VkDeviceMemory mem;
VkImageView view;
int32_t tex_width, tex_height, numMips_;
VkFormat format_;
VkImage mappableImage;
VkDeviceMemory mappableMemory;
VkMemoryRequirements mem_reqs;
bool needStaging;
};

View File

@ -0,0 +1,403 @@
// Copyright (c) 2016- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "Common/Vulkan/VulkanLoader.h"
#include "base/logging.h"
#ifndef _WIN32
#include <dlfcn.h>
#endif
PFN_vkCreateInstance vkCreateInstance;
PFN_vkDestroyInstance vkDestroyInstance;
PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices;
PFN_vkGetPhysicalDeviceFeatures vkGetPhysicalDeviceFeatures;
PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties;
PFN_vkGetPhysicalDeviceImageFormatProperties vkGetPhysicalDeviceImageFormatProperties;
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties;
PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
PFN_vkCreateDevice vkCreateDevice;
PFN_vkDestroyDevice vkDestroyDevice;
PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties;
PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties;
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
PFN_vkEnumerateDeviceLayerProperties vkEnumerateDeviceLayerProperties;
PFN_vkGetDeviceQueue vkGetDeviceQueue;
PFN_vkQueueSubmit vkQueueSubmit;
PFN_vkQueueWaitIdle vkQueueWaitIdle;
PFN_vkDeviceWaitIdle vkDeviceWaitIdle;
PFN_vkAllocateMemory vkAllocateMemory;
PFN_vkFreeMemory vkFreeMemory;
PFN_vkMapMemory vkMapMemory;
PFN_vkUnmapMemory vkUnmapMemory;
PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
PFN_vkGetDeviceMemoryCommitment vkGetDeviceMemoryCommitment;
PFN_vkBindBufferMemory vkBindBufferMemory;
PFN_vkBindImageMemory vkBindImageMemory;
PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
PFN_vkGetImageSparseMemoryRequirements vkGetImageSparseMemoryRequirements;
PFN_vkGetPhysicalDeviceSparseImageFormatProperties vkGetPhysicalDeviceSparseImageFormatProperties;
PFN_vkQueueBindSparse vkQueueBindSparse;
PFN_vkCreateFence vkCreateFence;
PFN_vkDestroyFence vkDestroyFence;
PFN_vkResetFences vkResetFences;
PFN_vkGetFenceStatus vkGetFenceStatus;
PFN_vkWaitForFences vkWaitForFences;
PFN_vkCreateSemaphore vkCreateSemaphore;
PFN_vkDestroySemaphore vkDestroySemaphore;
PFN_vkCreateEvent vkCreateEvent;
PFN_vkDestroyEvent vkDestroyEvent;
PFN_vkGetEventStatus vkGetEventStatus;
PFN_vkSetEvent vkSetEvent;
PFN_vkResetEvent vkResetEvent;
PFN_vkCreateQueryPool vkCreateQueryPool;
PFN_vkDestroyQueryPool vkDestroyQueryPool;
PFN_vkGetQueryPoolResults vkGetQueryPoolResults;
PFN_vkCreateBuffer vkCreateBuffer;
PFN_vkDestroyBuffer vkDestroyBuffer;
PFN_vkCreateBufferView vkCreateBufferView;
PFN_vkDestroyBufferView vkDestroyBufferView;
PFN_vkCreateImage vkCreateImage;
PFN_vkDestroyImage vkDestroyImage;
PFN_vkGetImageSubresourceLayout vkGetImageSubresourceLayout;
PFN_vkCreateImageView vkCreateImageView;
PFN_vkDestroyImageView vkDestroyImageView;
PFN_vkCreateShaderModule vkCreateShaderModule;
PFN_vkDestroyShaderModule vkDestroyShaderModule;
PFN_vkCreatePipelineCache vkCreatePipelineCache;
PFN_vkDestroyPipelineCache vkDestroyPipelineCache;
PFN_vkGetPipelineCacheData vkGetPipelineCacheData;
PFN_vkMergePipelineCaches vkMergePipelineCaches;
PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines;
PFN_vkCreateComputePipelines vkCreateComputePipelines;
PFN_vkDestroyPipeline vkDestroyPipeline;
PFN_vkCreatePipelineLayout vkCreatePipelineLayout;
PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout;
PFN_vkCreateSampler vkCreateSampler;
PFN_vkDestroySampler vkDestroySampler;
PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout;
PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout;
PFN_vkCreateDescriptorPool vkCreateDescriptorPool;
PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool;
PFN_vkResetDescriptorPool vkResetDescriptorPool;
PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets;
PFN_vkFreeDescriptorSets vkFreeDescriptorSets;
PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets;
PFN_vkCreateFramebuffer vkCreateFramebuffer;
PFN_vkDestroyFramebuffer vkDestroyFramebuffer;
PFN_vkCreateRenderPass vkCreateRenderPass;
PFN_vkDestroyRenderPass vkDestroyRenderPass;
PFN_vkGetRenderAreaGranularity vkGetRenderAreaGranularity;
PFN_vkCreateCommandPool vkCreateCommandPool;
PFN_vkDestroyCommandPool vkDestroyCommandPool;
PFN_vkResetCommandPool vkResetCommandPool;
PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers;
PFN_vkFreeCommandBuffers vkFreeCommandBuffers;
PFN_vkBeginCommandBuffer vkBeginCommandBuffer;
PFN_vkEndCommandBuffer vkEndCommandBuffer;
PFN_vkResetCommandBuffer vkResetCommandBuffer;
PFN_vkCmdBindPipeline vkCmdBindPipeline;
PFN_vkCmdSetViewport vkCmdSetViewport;
PFN_vkCmdSetScissor vkCmdSetScissor;
PFN_vkCmdSetLineWidth vkCmdSetLineWidth;
PFN_vkCmdSetDepthBias vkCmdSetDepthBias;
PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants;
PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds;
PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask;
PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask;
PFN_vkCmdSetStencilReference vkCmdSetStencilReference;
PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets;
PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer;
PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers;
PFN_vkCmdDraw vkCmdDraw;
PFN_vkCmdDrawIndexed vkCmdDrawIndexed;
PFN_vkCmdDrawIndirect vkCmdDrawIndirect;
PFN_vkCmdDrawIndexedIndirect vkCmdDrawIndexedIndirect;
PFN_vkCmdDispatch vkCmdDispatch;
PFN_vkCmdDispatchIndirect vkCmdDispatchIndirect;
PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
PFN_vkCmdCopyImage vkCmdCopyImage;
PFN_vkCmdBlitImage vkCmdBlitImage;
PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage;
PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer;
PFN_vkCmdUpdateBuffer vkCmdUpdateBuffer;
PFN_vkCmdFillBuffer vkCmdFillBuffer;
PFN_vkCmdClearColorImage vkCmdClearColorImage;
PFN_vkCmdClearDepthStencilImage vkCmdClearDepthStencilImage;
PFN_vkCmdClearAttachments vkCmdClearAttachments;
PFN_vkCmdResolveImage vkCmdResolveImage;
PFN_vkCmdSetEvent vkCmdSetEvent;
PFN_vkCmdResetEvent vkCmdResetEvent;
PFN_vkCmdWaitEvents vkCmdWaitEvents;
PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier;
PFN_vkCmdBeginQuery vkCmdBeginQuery;
PFN_vkCmdEndQuery vkCmdEndQuery;
PFN_vkCmdResetQueryPool vkCmdResetQueryPool;
PFN_vkCmdWriteTimestamp vkCmdWriteTimestamp;
PFN_vkCmdCopyQueryPoolResults vkCmdCopyQueryPoolResults;
PFN_vkCmdPushConstants vkCmdPushConstants;
PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass;
PFN_vkCmdNextSubpass vkCmdNextSubpass;
PFN_vkCmdEndRenderPass vkCmdEndRenderPass;
PFN_vkCmdExecuteCommands vkCmdExecuteCommands;
#ifdef ANDROID
PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHR;
#elif defined(_WIN32)
PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR;
#endif
PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR;
// WSI extension.
PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR;
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR;
PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR;
PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR;
PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR;
PFN_vkQueuePresentKHR vkQueuePresentKHR;
// And the DEBUG_REPORT extension.
PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT;
PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT;
#ifdef _WIN32
static HINSTANCE vulkanLibrary;
#define dlsym(x, y) GetProcAddress(x, y)
#else
static void *vulkanLibrary;
#endif
#define LOAD_INSTANCE_FUNC(instance, x) x = (PFN_ ## x)vkGetInstanceProcAddr(instance, #x); if (!x) {ILOG("Missing (instance): %s", #x);}
#define LOAD_DEVICE_FUNC(instance, x) x = (PFN_ ## x)vkGetDeviceProcAddr(instance, #x); if (!x) {ILOG("Missing (device): %s", #x);}
#define LOAD_GLOBAL_FUNC(x) x = (PFN_ ## x)dlsym(vulkanLibrary, #x); if (!x) {ILOG("Missing (global): %s", #x);}
bool VulkanLoad() {
#ifndef _WIN32
vulkanLibrary = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
#else
// LoadLibrary etc
vulkanLibrary = LoadLibrary(L"vulkan-1.dll");
#endif
if (!vulkanLibrary) {
return false;
}
LOAD_GLOBAL_FUNC(vkCreateInstance);
LOAD_GLOBAL_FUNC(vkGetInstanceProcAddr);
LOAD_GLOBAL_FUNC(vkGetDeviceProcAddr);
LOAD_GLOBAL_FUNC(vkEnumerateInstanceExtensionProperties);
LOAD_GLOBAL_FUNC(vkEnumerateInstanceLayerProperties);
WLOG("Vulkan base functions loaded.");
return true;
}
void VulkanLoadInstanceFunctions(VkInstance instance) {
// OK, let's use the above functions to get the rest.
LOAD_INSTANCE_FUNC(instance, vkDestroyInstance);
LOAD_INSTANCE_FUNC(instance, vkEnumeratePhysicalDevices);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceFeatures);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceFormatProperties);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceImageFormatProperties);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceProperties);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceQueueFamilyProperties);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceMemoryProperties);
LOAD_INSTANCE_FUNC(instance, vkCreateDevice);
LOAD_INSTANCE_FUNC(instance, vkDestroyDevice);
LOAD_INSTANCE_FUNC(instance, vkEnumerateDeviceExtensionProperties);
LOAD_INSTANCE_FUNC(instance, vkEnumerateDeviceLayerProperties);
LOAD_INSTANCE_FUNC(instance, vkGetDeviceQueue);
LOAD_INSTANCE_FUNC(instance, vkQueueSubmit);
LOAD_INSTANCE_FUNC(instance, vkQueueWaitIdle);
LOAD_INSTANCE_FUNC(instance, vkDeviceWaitIdle);
LOAD_INSTANCE_FUNC(instance, vkAllocateMemory);
LOAD_INSTANCE_FUNC(instance, vkFreeMemory);
LOAD_INSTANCE_FUNC(instance, vkMapMemory);
LOAD_INSTANCE_FUNC(instance, vkUnmapMemory);
LOAD_INSTANCE_FUNC(instance, vkFlushMappedMemoryRanges);
LOAD_INSTANCE_FUNC(instance, vkInvalidateMappedMemoryRanges);
LOAD_INSTANCE_FUNC(instance, vkGetDeviceMemoryCommitment);
LOAD_INSTANCE_FUNC(instance, vkBindBufferMemory);
LOAD_INSTANCE_FUNC(instance, vkBindImageMemory);
LOAD_INSTANCE_FUNC(instance, vkGetBufferMemoryRequirements);
LOAD_INSTANCE_FUNC(instance, vkGetImageMemoryRequirements);
LOAD_INSTANCE_FUNC(instance, vkGetImageSparseMemoryRequirements);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceSparseImageFormatProperties);
LOAD_INSTANCE_FUNC(instance, vkQueueBindSparse);
LOAD_INSTANCE_FUNC(instance, vkCreateFence);
LOAD_INSTANCE_FUNC(instance, vkDestroyFence);
LOAD_INSTANCE_FUNC(instance, vkResetFences);
LOAD_INSTANCE_FUNC(instance, vkGetFenceStatus);
LOAD_INSTANCE_FUNC(instance, vkWaitForFences);
LOAD_INSTANCE_FUNC(instance, vkCreateSemaphore);
LOAD_INSTANCE_FUNC(instance, vkDestroySemaphore);
LOAD_INSTANCE_FUNC(instance, vkCreateEvent);
LOAD_INSTANCE_FUNC(instance, vkDestroyEvent);
LOAD_INSTANCE_FUNC(instance, vkGetEventStatus);
LOAD_INSTANCE_FUNC(instance, vkSetEvent);
LOAD_INSTANCE_FUNC(instance, vkResetEvent);
LOAD_INSTANCE_FUNC(instance, vkCreateQueryPool);
LOAD_INSTANCE_FUNC(instance, vkDestroyQueryPool);
LOAD_INSTANCE_FUNC(instance, vkGetQueryPoolResults);
LOAD_INSTANCE_FUNC(instance, vkCreateBuffer);
LOAD_INSTANCE_FUNC(instance, vkDestroyBuffer);
LOAD_INSTANCE_FUNC(instance, vkCreateBufferView);
LOAD_INSTANCE_FUNC(instance, vkDestroyBufferView);
LOAD_INSTANCE_FUNC(instance, vkCreateImage);
LOAD_INSTANCE_FUNC(instance, vkDestroyImage);
LOAD_INSTANCE_FUNC(instance, vkGetImageSubresourceLayout);
LOAD_INSTANCE_FUNC(instance, vkCreateImageView);
LOAD_INSTANCE_FUNC(instance, vkDestroyImageView);
LOAD_INSTANCE_FUNC(instance, vkCreateShaderModule);
LOAD_INSTANCE_FUNC(instance, vkDestroyShaderModule);
LOAD_INSTANCE_FUNC(instance, vkCreatePipelineCache);
LOAD_INSTANCE_FUNC(instance, vkDestroyPipelineCache);
LOAD_INSTANCE_FUNC(instance, vkGetPipelineCacheData);
LOAD_INSTANCE_FUNC(instance, vkMergePipelineCaches);
LOAD_INSTANCE_FUNC(instance, vkCreateGraphicsPipelines);
LOAD_INSTANCE_FUNC(instance, vkCreateComputePipelines);
LOAD_INSTANCE_FUNC(instance, vkDestroyPipeline);
LOAD_INSTANCE_FUNC(instance, vkCreatePipelineLayout);
LOAD_INSTANCE_FUNC(instance, vkDestroyPipelineLayout);
LOAD_INSTANCE_FUNC(instance, vkCreateSampler);
LOAD_INSTANCE_FUNC(instance, vkDestroySampler);
LOAD_INSTANCE_FUNC(instance, vkCreateDescriptorSetLayout);
LOAD_INSTANCE_FUNC(instance, vkDestroyDescriptorSetLayout);
LOAD_INSTANCE_FUNC(instance, vkCreateDescriptorPool);
LOAD_INSTANCE_FUNC(instance, vkDestroyDescriptorPool);
LOAD_INSTANCE_FUNC(instance, vkResetDescriptorPool);
LOAD_INSTANCE_FUNC(instance, vkAllocateDescriptorSets);
LOAD_INSTANCE_FUNC(instance, vkFreeDescriptorSets);
LOAD_INSTANCE_FUNC(instance, vkUpdateDescriptorSets);
LOAD_INSTANCE_FUNC(instance, vkCreateFramebuffer);
LOAD_INSTANCE_FUNC(instance, vkDestroyFramebuffer);
LOAD_INSTANCE_FUNC(instance, vkCreateRenderPass);
LOAD_INSTANCE_FUNC(instance, vkDestroyRenderPass);
LOAD_INSTANCE_FUNC(instance, vkGetRenderAreaGranularity);
LOAD_INSTANCE_FUNC(instance, vkCreateCommandPool);
LOAD_INSTANCE_FUNC(instance, vkDestroyCommandPool);
LOAD_INSTANCE_FUNC(instance, vkResetCommandPool);
LOAD_INSTANCE_FUNC(instance, vkAllocateCommandBuffers);
LOAD_INSTANCE_FUNC(instance, vkFreeCommandBuffers);
LOAD_INSTANCE_FUNC(instance, vkBeginCommandBuffer);
LOAD_INSTANCE_FUNC(instance, vkEndCommandBuffer);
LOAD_INSTANCE_FUNC(instance, vkResetCommandBuffer);
LOAD_INSTANCE_FUNC(instance, vkCmdBindPipeline);
LOAD_INSTANCE_FUNC(instance, vkCmdSetViewport);
LOAD_INSTANCE_FUNC(instance, vkCmdSetScissor);
LOAD_INSTANCE_FUNC(instance, vkCmdSetLineWidth);
LOAD_INSTANCE_FUNC(instance, vkCmdSetDepthBias);
LOAD_INSTANCE_FUNC(instance, vkCmdSetBlendConstants);
LOAD_INSTANCE_FUNC(instance, vkCmdSetDepthBounds);
LOAD_INSTANCE_FUNC(instance, vkCmdSetStencilCompareMask);
LOAD_INSTANCE_FUNC(instance, vkCmdSetStencilWriteMask);
LOAD_INSTANCE_FUNC(instance, vkCmdSetStencilReference);
LOAD_INSTANCE_FUNC(instance, vkCmdBindDescriptorSets);
LOAD_INSTANCE_FUNC(instance, vkCmdBindIndexBuffer);
LOAD_INSTANCE_FUNC(instance, vkCmdBindVertexBuffers);
LOAD_INSTANCE_FUNC(instance, vkCmdDraw);
LOAD_INSTANCE_FUNC(instance, vkCmdDrawIndexed);
LOAD_INSTANCE_FUNC(instance, vkCmdDrawIndirect);
LOAD_INSTANCE_FUNC(instance, vkCmdDrawIndexedIndirect);
LOAD_INSTANCE_FUNC(instance, vkCmdDispatch);
LOAD_INSTANCE_FUNC(instance, vkCmdDispatchIndirect);
LOAD_INSTANCE_FUNC(instance, vkCmdCopyBuffer);
LOAD_INSTANCE_FUNC(instance, vkCmdCopyImage);
LOAD_INSTANCE_FUNC(instance, vkCmdBlitImage);
LOAD_INSTANCE_FUNC(instance, vkCmdCopyBufferToImage);
LOAD_INSTANCE_FUNC(instance, vkCmdCopyImageToBuffer);
LOAD_INSTANCE_FUNC(instance, vkCmdUpdateBuffer);
LOAD_INSTANCE_FUNC(instance, vkCmdFillBuffer);
LOAD_INSTANCE_FUNC(instance, vkCmdClearColorImage);
LOAD_INSTANCE_FUNC(instance, vkCmdClearDepthStencilImage);
LOAD_INSTANCE_FUNC(instance, vkCmdClearAttachments);
LOAD_INSTANCE_FUNC(instance, vkCmdResolveImage);
LOAD_INSTANCE_FUNC(instance, vkCmdSetEvent);
LOAD_INSTANCE_FUNC(instance, vkCmdResetEvent);
LOAD_INSTANCE_FUNC(instance, vkCmdWaitEvents);
LOAD_INSTANCE_FUNC(instance, vkCmdPipelineBarrier);
LOAD_INSTANCE_FUNC(instance, vkCmdBeginQuery);
LOAD_INSTANCE_FUNC(instance, vkCmdEndQuery);
LOAD_INSTANCE_FUNC(instance, vkCmdResetQueryPool);
LOAD_INSTANCE_FUNC(instance, vkCmdWriteTimestamp);
LOAD_INSTANCE_FUNC(instance, vkCmdCopyQueryPoolResults);
LOAD_INSTANCE_FUNC(instance, vkCmdPushConstants);
LOAD_INSTANCE_FUNC(instance, vkCmdBeginRenderPass);
LOAD_INSTANCE_FUNC(instance, vkCmdNextSubpass);
LOAD_INSTANCE_FUNC(instance, vkCmdEndRenderPass);
LOAD_INSTANCE_FUNC(instance, vkCmdExecuteCommands);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceSurfaceSupportKHR);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceSurfaceFormatsKHR);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceSurfacePresentModesKHR);
LOAD_INSTANCE_FUNC(instance, vkCreateSwapchainKHR);
LOAD_INSTANCE_FUNC(instance, vkDestroySwapchainKHR);
LOAD_INSTANCE_FUNC(instance, vkGetSwapchainImagesKHR);
LOAD_INSTANCE_FUNC(instance, vkAcquireNextImageKHR);
LOAD_INSTANCE_FUNC(instance, vkQueuePresentKHR);
#ifdef _WIN32
LOAD_INSTANCE_FUNC(instance, vkCreateWin32SurfaceKHR);
#elif defined(ANDROID)
LOAD_INSTANCE_FUNC(instance, vkCreateAndroidSurfaceKHR);
#endif
LOAD_INSTANCE_FUNC(instance, vkDestroySurfaceKHR);
LOAD_INSTANCE_FUNC(instance, vkCreateDebugReportCallbackEXT);
LOAD_INSTANCE_FUNC(instance, vkDestroyDebugReportCallbackEXT);
WLOG("Vulkan instance functions loaded.");
}
// On some implementations, loading functions (that have Device as their first parameter) via vkGetDeviceProcAddr may
// increase performance - but then these function pointers will only work on that specific device. Thus, this loader is not very
// good for multi-device.
void VulkanLoadDeviceFunctions(VkDevice device) {
WLOG("Vulkan device functions loaded.");
// TODO: Move more functions VulkanLoadInstanceFunctions to here.
LOAD_DEVICE_FUNC(device, vkCreateSwapchainKHR);
LOAD_DEVICE_FUNC(device, vkDestroySwapchainKHR);
LOAD_DEVICE_FUNC(device, vkGetSwapchainImagesKHR);
LOAD_DEVICE_FUNC(device, vkAcquireNextImageKHR);
LOAD_DEVICE_FUNC(device, vkQueuePresentKHR);
}
void VulkanFree() {
#ifdef _WIN32
if (vulkanLibrary) {
FreeLibrary(vulkanLibrary);
}
#else
if (vulkanLibrary) {
dlclose(vulkanLibrary);
}
#endif
}

View File

@ -0,0 +1,198 @@
// Copyright (c) 2016- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#ifdef ANDROID
#define VK_USE_PLATFORM_ANDROID_KHR
#elif defined(_WIN32)
#define VK_USE_PLATFORM_WIN32_KHR
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#endif
#define VK_NO_PROTOTYPES
#include "ext/vulkan/vulkan.h"
extern PFN_vkCreateInstance vkCreateInstance;
extern PFN_vkDestroyInstance vkDestroyInstance;
extern PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices;
extern PFN_vkGetPhysicalDeviceFeatures vkGetPhysicalDeviceFeatures;
extern PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties;
extern PFN_vkGetPhysicalDeviceImageFormatProperties vkGetPhysicalDeviceImageFormatProperties;
extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
extern PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties;
extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
extern PFN_vkCreateDevice vkCreateDevice;
extern PFN_vkDestroyDevice vkDestroyDevice;
extern PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties;
extern PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties;
extern PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
extern PFN_vkEnumerateDeviceLayerProperties vkEnumerateDeviceLayerProperties;
extern PFN_vkGetDeviceQueue vkGetDeviceQueue;
extern PFN_vkQueueSubmit vkQueueSubmit;
extern PFN_vkQueueWaitIdle vkQueueWaitIdle;
extern PFN_vkDeviceWaitIdle vkDeviceWaitIdle;
extern PFN_vkAllocateMemory vkAllocateMemory;
extern PFN_vkFreeMemory vkFreeMemory;
extern PFN_vkMapMemory vkMapMemory;
extern PFN_vkUnmapMemory vkUnmapMemory;
extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
extern PFN_vkGetDeviceMemoryCommitment vkGetDeviceMemoryCommitment;
extern PFN_vkBindBufferMemory vkBindBufferMemory;
extern PFN_vkBindImageMemory vkBindImageMemory;
extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
extern PFN_vkGetImageSparseMemoryRequirements vkGetImageSparseMemoryRequirements;
extern PFN_vkGetPhysicalDeviceSparseImageFormatProperties vkGetPhysicalDeviceSparseImageFormatProperties;
extern PFN_vkQueueBindSparse vkQueueBindSparse;
extern PFN_vkCreateFence vkCreateFence;
extern PFN_vkDestroyFence vkDestroyFence;
extern PFN_vkResetFences vkResetFences;
extern PFN_vkGetFenceStatus vkGetFenceStatus;
extern PFN_vkWaitForFences vkWaitForFences;
extern PFN_vkCreateSemaphore vkCreateSemaphore;
extern PFN_vkDestroySemaphore vkDestroySemaphore;
extern PFN_vkCreateEvent vkCreateEvent;
extern PFN_vkDestroyEvent vkDestroyEvent;
extern PFN_vkGetEventStatus vkGetEventStatus;
extern PFN_vkSetEvent vkSetEvent;
extern PFN_vkResetEvent vkResetEvent;
extern PFN_vkCreateQueryPool vkCreateQueryPool;
extern PFN_vkDestroyQueryPool vkDestroyQueryPool;
extern PFN_vkGetQueryPoolResults vkGetQueryPoolResults;
extern PFN_vkCreateBuffer vkCreateBuffer;
extern PFN_vkDestroyBuffer vkDestroyBuffer;
extern PFN_vkCreateBufferView vkCreateBufferView;
extern PFN_vkDestroyBufferView vkDestroyBufferView;
extern PFN_vkCreateImage vkCreateImage;
extern PFN_vkDestroyImage vkDestroyImage;
extern PFN_vkGetImageSubresourceLayout vkGetImageSubresourceLayout;
extern PFN_vkCreateImageView vkCreateImageView;
extern PFN_vkDestroyImageView vkDestroyImageView;
extern PFN_vkCreateShaderModule vkCreateShaderModule;
extern PFN_vkDestroyShaderModule vkDestroyShaderModule;
extern PFN_vkCreatePipelineCache vkCreatePipelineCache;
extern PFN_vkDestroyPipelineCache vkDestroyPipelineCache;
extern PFN_vkGetPipelineCacheData vkGetPipelineCacheData;
extern PFN_vkMergePipelineCaches vkMergePipelineCaches;
extern PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines;
extern PFN_vkCreateComputePipelines vkCreateComputePipelines;
extern PFN_vkDestroyPipeline vkDestroyPipeline;
extern PFN_vkCreatePipelineLayout vkCreatePipelineLayout;
extern PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout;
extern PFN_vkCreateSampler vkCreateSampler;
extern PFN_vkDestroySampler vkDestroySampler;
extern PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout;
extern PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout;
extern PFN_vkCreateDescriptorPool vkCreateDescriptorPool;
extern PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool;
extern PFN_vkResetDescriptorPool vkResetDescriptorPool;
extern PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets;
extern PFN_vkFreeDescriptorSets vkFreeDescriptorSets;
extern PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets;
extern PFN_vkCreateFramebuffer vkCreateFramebuffer;
extern PFN_vkDestroyFramebuffer vkDestroyFramebuffer;
extern PFN_vkCreateRenderPass vkCreateRenderPass;
extern PFN_vkDestroyRenderPass vkDestroyRenderPass;
extern PFN_vkGetRenderAreaGranularity vkGetRenderAreaGranularity;
extern PFN_vkCreateCommandPool vkCreateCommandPool;
extern PFN_vkDestroyCommandPool vkDestroyCommandPool;
extern PFN_vkResetCommandPool vkResetCommandPool;
extern PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers;
extern PFN_vkFreeCommandBuffers vkFreeCommandBuffers;
extern PFN_vkBeginCommandBuffer vkBeginCommandBuffer;
extern PFN_vkEndCommandBuffer vkEndCommandBuffer;
extern PFN_vkResetCommandBuffer vkResetCommandBuffer;
extern PFN_vkCmdBindPipeline vkCmdBindPipeline;
extern PFN_vkCmdSetViewport vkCmdSetViewport;
extern PFN_vkCmdSetScissor vkCmdSetScissor;
extern PFN_vkCmdSetLineWidth vkCmdSetLineWidth;
extern PFN_vkCmdSetDepthBias vkCmdSetDepthBias;
extern PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants;
extern PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds;
extern PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask;
extern PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask;
extern PFN_vkCmdSetStencilReference vkCmdSetStencilReference;
extern PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets;
extern PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer;
extern PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers;
extern PFN_vkCmdDraw vkCmdDraw;
extern PFN_vkCmdDrawIndexed vkCmdDrawIndexed;
extern PFN_vkCmdDrawIndirect vkCmdDrawIndirect;
extern PFN_vkCmdDrawIndexedIndirect vkCmdDrawIndexedIndirect;
extern PFN_vkCmdDispatch vkCmdDispatch;
extern PFN_vkCmdDispatchIndirect vkCmdDispatchIndirect;
extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
extern PFN_vkCmdCopyImage vkCmdCopyImage;
extern PFN_vkCmdBlitImage vkCmdBlitImage;
extern PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage;
extern PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer;
extern PFN_vkCmdUpdateBuffer vkCmdUpdateBuffer;
extern PFN_vkCmdFillBuffer vkCmdFillBuffer;
extern PFN_vkCmdClearColorImage vkCmdClearColorImage;
extern PFN_vkCmdClearDepthStencilImage vkCmdClearDepthStencilImage;
extern PFN_vkCmdClearAttachments vkCmdClearAttachments;
extern PFN_vkCmdResolveImage vkCmdResolveImage;
extern PFN_vkCmdSetEvent vkCmdSetEvent;
extern PFN_vkCmdResetEvent vkCmdResetEvent;
extern PFN_vkCmdWaitEvents vkCmdWaitEvents;
extern PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier;
extern PFN_vkCmdBeginQuery vkCmdBeginQuery;
extern PFN_vkCmdEndQuery vkCmdEndQuery;
extern PFN_vkCmdResetQueryPool vkCmdResetQueryPool;
extern PFN_vkCmdWriteTimestamp vkCmdWriteTimestamp;
extern PFN_vkCmdCopyQueryPoolResults vkCmdCopyQueryPoolResults;
extern PFN_vkCmdPushConstants vkCmdPushConstants;
extern PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass;
extern PFN_vkCmdNextSubpass vkCmdNextSubpass;
extern PFN_vkCmdEndRenderPass vkCmdEndRenderPass;
extern PFN_vkCmdExecuteCommands vkCmdExecuteCommands;
#ifdef ANDROID
extern PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHR;
#elif defined(_WIN32)
extern PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR;
#endif
extern PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR;
// Simple loader for the WSI extension.
extern PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR;
extern PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
extern PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
extern PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
extern PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR;
extern PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR;
extern PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR;
extern PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR;
extern PFN_vkQueuePresentKHR vkQueuePresentKHR;
// And the DEBUG_REPORT extension.
extern PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT;
extern PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT;
bool VulkanLoad();
void VulkanLoadInstanceFunctions(VkInstance instance);
void VulkanLoadDeviceFunctions(VkDevice device);
void VulkanFree();

View File

@ -0,0 +1,48 @@
// Copyright (c) 2016- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
// Additionally, Common/Vulkan/* , including this file, are also licensed
// under the public domain.
#include "Common/Vulkan/VulkanMemory.h"
VulkanPushBuffer::VulkanPushBuffer(VulkanContext *vulkan, size_t size) : offset_(0), size_(size), writePtr_(nullptr), deviceMemory_(0) {
VkDevice device = vulkan->GetDevice();
VkBufferCreateInfo b = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
b.size = size;
b.flags = 0;
b.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
b.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
b.queueFamilyIndexCount = 0;
b.pQueueFamilyIndices = nullptr;
VkResult res = vkCreateBuffer(device, &b, nullptr, &buffer_);
assert(VK_SUCCESS == res);
// Okay, that's the buffer. Now let's allocate some memory for it.
VkMemoryAllocateInfo alloc = {};
alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc.pNext = nullptr;
vulkan->MemoryTypeFromProperties(0xFFFFFFFF, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &alloc.memoryTypeIndex);
alloc.allocationSize = size;
res = vkAllocateMemory(device, &alloc, nullptr, &deviceMemory_);
assert(VK_SUCCESS == res);
res = vkBindBufferMemory(device, buffer_, deviceMemory_, 0);
assert(VK_SUCCESS == res);
}

View File

@ -0,0 +1,102 @@
#pragma once
#include "Common/Vulkan/VulkanContext.h"
// VulkanMemory
//
// Vulkan memory management utils.
// VulkanPushBuffer
// Simple incrementing allocator.
// Use these to push vertex, index and uniform data. Generally you'll have two of these
// and alternate on each frame. Make sure not to reset until the fence from the last time you used it
// has completed.
//
// TODO: Make it possible to suballocate pushbuffers from a large DeviceMemory block.
// TODO: Make this auto-grow and shrink. Need to be careful about returning and using the new
// buffer handle on overflow.
class VulkanPushBuffer {
public:
VulkanPushBuffer(VulkanContext *vulkan, size_t size);
~VulkanPushBuffer() {
assert(buffer_ == VK_NULL_HANDLE);
assert(deviceMemory_ == VK_NULL_HANDLE);
}
void Destroy(VulkanContext *vulkan) {
vulkan->Delete().QueueDeleteBuffer(buffer_);
vulkan->Delete().QueueDeleteDeviceMemory(deviceMemory_);
buffer_ = VK_NULL_HANDLE;
deviceMemory_ = VK_NULL_HANDLE;
}
void Reset() { offset_ = 0; }
void Begin(VkDevice device) {
offset_ = 0;
VkResult res = vkMapMemory(device, deviceMemory_, 0, size_, 0, (void **)(&writePtr_));
assert(VK_SUCCESS == res);
}
void End(VkDevice device) {
/*
VkMappedMemoryRange range = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
range.offset = 0;
range.size = offset_;
range.memory = deviceMemory_;
vkFlushMappedMemoryRanges(device, 1, &range);
*/
vkUnmapMemory(device, deviceMemory_);
writePtr_ = nullptr;
}
// When using the returned memory, make sure to bind the returned vkbuf.
// This will later allow for handling overflow correctly.
size_t Allocate(size_t numBytes, VkBuffer *vkbuf) {
size_t out = offset_;
offset_ += (numBytes + 3) & ~3; // Round up to 4 bytes.
if (offset_ >= size_) {
// TODO: Allocate a second buffer, then combine them on the next frame.
#ifdef _WIN32
DebugBreak();
#endif
}
*vkbuf = buffer_;
return out;
}
// TODO: Add alignment support?
// Returns the offset that should be used when binding this buffer to get this data.
size_t Push(const void *data, size_t size, VkBuffer *vkbuf) {
size_t off = Allocate(size, vkbuf);
memcpy(writePtr_ + off, data, size);
return off;
}
uint32_t PushAligned(const void *data, size_t size, int align, VkBuffer *vkbuf) {
offset_ = (offset_ + align - 1) & ~(align - 1);
size_t off = Allocate(size, vkbuf);
memcpy(writePtr_ + off, data, size);
return (uint32_t)off;
}
size_t GetOffset() const {
return offset_;
}
// "Zero-copy" variant - you can write the data directly as you compute it.
void *Push(size_t size, uint32_t *bindOffset, VkBuffer *vkbuf) {
size_t off = Allocate(size, vkbuf);
*bindOffset = (uint32_t)off;
return writePtr_ + off;
}
private:
VkDeviceMemory deviceMemory_;
VkBuffer buffer_;
size_t offset_;
size_t size_;
uint8_t *writePtr_;
};

View File

@ -454,11 +454,11 @@ static ConfigSetting graphicsSettings[] = {
#endif
ReportedConfigSetting("ForceMaxEmulatedFPS", &g_Config.iForceMaxEmulatedFPS, 60, true, true),
// TODO: Hm, on fast mobile GPUs we should definitely default to at least 4...
// TODO: Hm, on fast mobile GPUs we should definitely default to at least 4 (setting = 2)...
#ifdef MOBILE_DEVICE
ConfigSetting("AnisotropyLevel", &g_Config.iAnisotropyLevel, 0, true, true),
#else
ConfigSetting("AnisotropyLevel", &g_Config.iAnisotropyLevel, 8, true, true),
ConfigSetting("AnisotropyLevel", &g_Config.iAnisotropyLevel, 4, true, true),
#endif
ReportedConfigSetting("VertexCache", &g_Config.bVertexCache, true, true, true),
ReportedConfigSetting("TextureBackoffCache", &g_Config.bTextureBackoffCache, false, true, true),
@ -862,6 +862,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) {
vPinnedPaths.push_back(it->second);
}
// This caps the exponent 4 (so 16x.)
if (iAnisotropyLevel > 4) {
iAnisotropyLevel = 4;
}

View File

@ -50,10 +50,14 @@ enum BufferFilter {
enum class GPUBackend {
OPENGL = 0,
DIRECT3D9 = 1,
DIRECT3D11 = 2,
VULKAN = 3,
};
enum {
GPU_BACKEND_OPENGL = (int)GPUBackend::OPENGL,
GPU_BACKEND_DIRECT3D9 = (int)GPUBackend::DIRECT3D9,
GPU_BACKEND_DIRECT3D11 = (int)GPUBackend::DIRECT3D11,
GPU_BACKEND_VULKAN = (int)GPUBackend::VULKAN,
};
enum AudioBackendType {

View File

@ -34,6 +34,7 @@
#ifdef _WIN32
#include "Windows/GPU/WindowsGLContext.h"
#include "Windows/GPU/D3D9Context.h"
#include "Windows/GPU/WindowsVulkanContext.h"
#include "Windows/InputDevice.h"
#endif

View File

@ -45,7 +45,6 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>false</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v140_xp</PlatformToolset>
</PropertyGroup>
@ -158,6 +157,7 @@
<PreprocessorDefinitions>USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_64=1;_M_X64=1;_LIB;NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StringPooling>true</StringPooling>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -737,4 +737,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -31,19 +31,23 @@ enum GPUCore {
GPU_GLES,
GPU_SOFTWARE,
GPU_DIRECTX9,
GPU_DIRECTX11,
GPU_VULKAN,
};
class FileLoader;
class GraphicsContext;
class Thin3DContext;
// PSP_CoreParameter()
struct CoreParameter {
CoreParameter() : collectEmuLog(0), unthrottle(false), fpsLimit(0), updateRecent(true), freezeNext(false), frozen(false), mountIsoLoader(nullptr) {}
CoreParameter() : thin3d(nullptr), collectEmuLog(0), unthrottle(false), fpsLimit(0), updateRecent(true), freezeNext(false), frozen(false), mountIsoLoader(nullptr) {}
CPUCore cpuCore;
GPUCore gpuCore;
GraphicsContext *graphicsContext; // TODO: Find a better place.
Thin3DContext *thin3d;
bool enableSound; // there aren't multiple sound cores.
std::string fileToStart;

View File

@ -439,7 +439,8 @@ void __DisplayGetDebugStats(char stats[], size_t bufsize) {
"Texture invalidations: %i\n"
"Vertex shaders loaded: %i\n"
"Fragment shaders loaded: %i\n"
"Combined shaders loaded: %i\n",
"Combined shaders loaded: %i\n"
"Pushbuffer space used: UBO %d, Vtx %d, Idx %d\n",
gpuStats.numVBlanks,
gpuStats.msProcessingDisplayLists * 1000.0f,
kernelStats.msInSyscalls * 1000.0f,
@ -463,7 +464,10 @@ void __DisplayGetDebugStats(char stats[], size_t bufsize) {
gpuStats.numTextureInvalidations,
gpuStats.numVertexShaders,
gpuStats.numFragmentShaders,
gpuStats.numShaders
gpuStats.numShaders,
gpuStats.pushUBOSpaceUsed,
gpuStats.pushVertexSpaceUsed,
gpuStats.pushIndexSpaceUsed
);
stats[bufsize - 1] = '\0';
gpuStats.ResetFrame();

View File

@ -386,7 +386,12 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) {
#else
INFO_LOG(BOOT, "PPSSPP %s", PPSSPP_GIT_VERSION);
#endif
GraphicsContext *temp = coreParameter.graphicsContext;
coreParameter = coreParam;
if (coreParameter.graphicsContext == nullptr) {
coreParameter.graphicsContext = temp;
}
coreParameter.errorString = "";
pspIsIniting = true;
@ -420,7 +425,7 @@ bool PSP_InitUpdate(std::string *error_string) {
bool success = coreParameter.fileToStart != "";
*error_string = coreParameter.errorString;
if (success) {
success = GPU_Init(coreParameter.graphicsContext);
success = GPU_Init(coreParameter.graphicsContext, coreParameter.thin3d);
if (!success) {
PSP_Shutdown();
*error_string = "Unable to initialize rendering engine.";

View File

@ -68,6 +68,8 @@ void PSP_BeginHostFrame();
void PSP_EndHostFrame();
void PSP_RunLoopUntil(u64 globalticks);
void PSP_RunLoopFor(int cycles);
void PSP_BeginFrame();
void PSP_EndFrame();
void Audio_Init();

View File

@ -27,7 +27,7 @@
#define WRITE p+=sprintf
// Uses integer instructions available since OpenGL 3.0. Suitable for ES 3.0 as well.
void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat) {
void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat, ShaderLanguage language) {
char *p = buffer;
if (gl_extensions.IsGLES) {
WRITE(p, "#version 300 es\n");
@ -250,7 +250,8 @@ void GenerateDepalShader(char *buffer, GEBufferFormat pixelFormat, ShaderLanguag
GenerateDepalShaderFloat(buffer, pixelFormat, language);
break;
case GLSL_300:
GenerateDepalShader300(buffer, pixelFormat);
case GLSL_VULKAN:
GenerateDepalShader300(buffer, pixelFormat, language);
break;
case HLSL_DX9:
GenerateDepalShaderFloat(buffer, pixelFormat, language);

View File

@ -22,6 +22,7 @@
enum ShaderLanguage {
GLSL_140,
GLSL_300,
GLSL_VULKAN,
HLSL_DX9,
};

View File

@ -51,6 +51,8 @@ namespace DX9 {
struct FBO_DX9;
}
class VulkanFBO;
struct VirtualFramebuffer {
int last_frame_used;
int last_frame_attached;
@ -85,11 +87,13 @@ struct VirtualFramebuffer {
int lastFrameNewSize;
GEBufferFormat format; // virtual, right now they are all RGBA8888
// TODO: Handle fbo and colorDepth better.
u8 colorDepth;
union {
FBO *fbo;
DX9::FBO_DX9 *fbo_dx9;
VulkanFBO *fbo_vk;
};
u16 drawnWidth;

View File

@ -544,6 +544,7 @@ float FromScaledDepth(float z) {
void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, float renderHeight, int bufferWidth, int bufferHeight, ViewportAndScissor &out) {
bool throughmode = gstate.isModeThrough();
out.dirtyProj = false;
out.dirtyDepth = false;
float renderWidthFactor, renderHeightFactor;
float renderX = 0.0f, renderY = 0.0f;
@ -640,31 +641,33 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo
float hScale = 1.0f;
float yOffset = 0.0f;
// If we're within the bounds, we want clipping the viewport way. So leave it be.
if (left < 0.0f || right > renderWidth) {
float overageLeft = std::max(-left, 0.0f);
float overageRight = std::max(right - renderWidth, 0.0f);
// Our center drifted by the difference in overages.
float drift = overageRight - overageLeft;
if (!gstate_c.Supports(GPU_SUPPORTS_LARGE_VIEWPORTS)) {
// If we're within the bounds, we want clipping the viewport way. So leave it be.
if (left < 0.0f || right > renderWidth) {
float overageLeft = std::max(-left, 0.0f);
float overageRight = std::max(right - renderWidth, 0.0f);
// Our center drifted by the difference in overages.
float drift = overageRight - overageLeft;
left += overageLeft;
right -= overageRight;
left += overageLeft;
right -= overageRight;
wScale = vpWidth / (right - left);
xOffset = drift / (right - left);
}
wScale = vpWidth / (right - left);
xOffset = drift / (right - left);
}
if (top < 0.0f || bottom > renderHeight) {
float overageTop = std::max(-top, 0.0f);
float overageBottom = std::max(bottom - renderHeight, 0.0f);
// Our center drifted by the difference in overages.
float drift = overageBottom - overageTop;
if (top < 0.0f || bottom > renderHeight) {
float overageTop = std::max(-top, 0.0f);
float overageBottom = std::max(bottom - renderHeight, 0.0f);
// Our center drifted by the difference in overages.
float drift = overageBottom - overageTop;
top += overageTop;
bottom -= overageBottom;
top += overageTop;
bottom -= overageBottom;
hScale = vpHeight / (bottom - top);
yOffset = drift / (bottom - top);
hScale = vpHeight / (bottom - top);
yOffset = drift / (bottom - top);
}
}
out.viewportX = left + displayOffsetX;

View File

@ -22,6 +22,7 @@ enum DebugShaderType {
SHADER_TYPE_FRAGMENT = 1,
SHADER_TYPE_GEOMETRY = 2,
SHADER_TYPE_VERTEXLOADER = 3, // Not really a shader, but might as well re-use this mechanism
SHADER_TYPE_PIPELINE = 4, // Vulkan and DX12 combines a bunch of state into pipeline objects. Might as well make them inspectable.
};
enum DebugShaderStringType {

View File

@ -122,7 +122,7 @@ void ComputeVertexShaderID(ShaderID *id_out, u32 vertType, bool useHWTransform)
if (doTextureProjection && gstate.getUVProjMode() == GE_PROJMAP_UV) {
id.SetBits(VS_BIT_TEXCOORD_FMTSCALE, 2, (vertType & GE_VTYPE_TC_MASK) >> GE_VTYPE_TC_SHIFT); // two bits
} else {
id.SetBits(VS_BIT_TEXCOORD_FMTSCALE, 2, 3);
id.SetBits(VS_BIT_TEXCOORD_FMTSCALE, 2, 3); // float - no scaling
}
}

View File

@ -129,8 +129,14 @@ static bool IsReallyAClear(const TransformedVertex *transformed, int numVerts) {
}
void SoftwareTransform(
int prim, u8 *decoded, int vertexCount, u32 vertType, u16 *&inds, int indexType,
const DecVtxFormat &decVtxFormat, int &maxIndex, FramebufferManagerCommon *fbman, TextureCacheCommon *texCache, TransformedVertex *transformed, TransformedVertex *transformedExpanded, TransformedVertex *&drawBuffer, int &numTrans, bool &drawIndexed, SoftwareTransformResult *result, float ySign) {
int prim, int vertexCount, u32 vertType, u16 *&inds, int indexType,
const DecVtxFormat &decVtxFormat, int &maxIndex, TransformedVertex *&drawBuffer, int &numTrans, bool &drawIndexed, const SoftwareTransformParams *params, SoftwareTransformResult *result) {
u8 *decoded = params->decoded;
FramebufferManagerCommon *fbman = params->fbman;
TextureCacheCommon *texCache = params->texCache;
TransformedVertex *transformed = params->transformed;
TransformedVertex *transformedExpanded = params->transformedExpanded;
float ySign = 1.0f;
bool throughmode = (vertType & GE_VTYPE_THROUGH_MASK) != 0;
bool lmode = gstate.isUsingSecondaryColor() && gstate.isLightingEnabled();
@ -406,11 +412,16 @@ void SoftwareTransform(
// Experiment: Disable on PowerVR (see issue #6290)
// TODO: This bleeds outside the play area in non-buffered mode. Big deal? Probably not.
if (maxIndex > 1 && gstate.isModeClear() && prim == GE_PRIM_RECTANGLES && IsReallyAClear(transformed, maxIndex) && gl_extensions.gpuVendor != GPU_VENDOR_POWERVR) { // && g_Config.iRenderingMode != FB_NON_BUFFERED_MODE) {
result->color = transformed[1].color0_32;
// Need to rescale from a [0, 1] float. This is the final transformed value.
result->depth = ToScaledDepth((s16)(int)(transformed[1].z * 65535.0f));
result->action = SW_CLEAR;
return;
// If alpha is not allowed to be separate, it must match for both depth/stencil and color. Vulkan requires this.
bool alphaMatchesColor = gstate.isClearModeColorMask() == gstate.isClearModeAlphaMask();
bool depthMatchesStencil = gstate.isClearModeAlphaMask() == gstate.isClearModeDepthMask();
if (params->allowSeparateAlphaClear || (alphaMatchesColor && depthMatchesStencil)) {
result->color = transformed[1].color0_32;
// Need to rescale from a [0, 1] float. This is the final transformed value.
result->depth = ToScaledDepth((s16)(int)(transformed[1].z * 65535.0f));
result->action = SW_CLEAR;
return;
}
}
// This means we're using a framebuffer (and one that isn't big enough.)

View File

@ -38,5 +38,14 @@ struct SoftwareTransformResult {
u8 stencilValue;
};
void SoftwareTransform(int prim, u8 *decoded, int vertexCount, u32 vertexType, u16 *&inds, int indexType, const DecVtxFormat &decVtxFormat, int &maxIndex, FramebufferManagerCommon *fbman, TextureCacheCommon *texCache, TransformedVertex *transformed, TransformedVertex *transformedExpanded, TransformedVertex *&drawBuffer,
int &numTrans, bool &drawIndexed, SoftwareTransformResult *result, float ySign);
struct SoftwareTransformParams {
u8 *decoded;
TransformedVertex *transformed;
TransformedVertex *transformedExpanded;
FramebufferManagerCommon *fbman;
TextureCacheCommon *texCache;
bool allowSeparateAlphaClear;
};
void SoftwareTransform(int prim, int vertexCount, u32 vertexType, u16 *&inds, int indexType, const DecVtxFormat &decVtxFormat, int &maxIndex, TransformedVertex *&drawBuffer,
int &numTrans, bool &drawIndexed, const SoftwareTransformParams *params, SoftwareTransformResult *result);

View File

@ -39,6 +39,8 @@ enum FramebufferNotification {
struct VirtualFramebuffer;
class CachedTextureVulkan;
class TextureCacheCommon {
public:
TextureCacheCommon();
@ -96,6 +98,7 @@ public:
union {
u32 textureName;
void *texturePtr;
CachedTextureVulkan *vkTex;
};
int invalidHint;
u32 fullhash;
@ -104,7 +107,7 @@ public:
u16 maxSeenV;
// Cache the current filter settings so we can avoid setting it again.
// (OpenGL madness where filter settings are attached to each texture).
// (OpenGL madness where filter settings are attached to each texture. Unused in Vulkan).
u8 magFilt;
u8 minFilt;
bool sClamp;

View File

@ -1118,7 +1118,7 @@ int VertexDecoder::ToString(char *output) const {
if (tc)
output += sprintf(output, "T: %s ", tcnames[tc]);
if (weighttype)
output += sprintf(output, "W: %s (%ix)", weightnames[weighttype], nweights);
output += sprintf(output, "W: %s (%ix) ", weightnames[weighttype], nweights);
if (idx)
output += sprintf(output, "I: %s ", idxnames[idx]);
if (morphcount > 1)

View File

@ -106,8 +106,7 @@ inline int RoundUp4(int x) {
}
// Reads decoded vertex formats in a convenient way. For software transform and debugging.
class VertexReader
{
class VertexReader {
public:
VertexReader(u8 *base, const DecVtxFormat &decFmt, int vtype) : base_(base), data_(base), decFmt_(decFmt), vtype_(vtype) {}

View File

@ -480,6 +480,24 @@ void DIRECTX9_GPU::CheckGPUFeatures() {
features |= GPU_SUPPORTS_TEXTURE_LOD_CONTROL;
features |= GPU_PREFER_CPU_DOWNLOAD;
features |= GPU_SUPPORTS_ACCURATE_DEPTH;
features |= GPU_SUPPORTS_UNPACK_SUBIMAGE;
D3DCAPS9 caps;
ZeroMemory(&caps, sizeof(caps));
HRESULT result = 0;
if (pD3DdeviceEx) {
result = pD3DdeviceEx->GetDeviceCaps(&caps);
} else {
result = pD3Ddevice->GetDeviceCaps(&caps);
}
if (FAILED(result)) {
WARN_LOG_REPORT(G3D, "Direct3D9: Failed to get the device caps!");
} else {
if ((caps.RasterCaps & D3DPRASTERCAPS_ANISOTROPY) != 0 && caps.MaxAnisotropy > 1)
features |= GPU_SUPPORTS_ANISOTROPY;
if ((caps.TextureCaps & (D3DPTEXTURECAPS_NONPOW2CONDITIONAL | D3DPTEXTURECAPS_POW2)) == 0)
features |= GPU_SUPPORTS_OES_TEXTURE_NPOT;
}
if (!g_Config.bHighQualityDepth) {
features |= GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT;

View File

@ -603,7 +603,7 @@ void TextureCacheDX9::UpdateSamplingParams(TexCacheEntry &entry, bool force) {
D3DTEXTUREFILTERTYPE mipf = (D3DTEXTUREFILTERTYPE)MipFilt[minFilt];
D3DTEXTUREFILTERTYPE magf = (D3DTEXTUREFILTERTYPE)MagFilt[magFilt];
if (g_Config.iAnisotropyLevel > 0 && minf == D3DTEXF_LINEAR) {
if (gstate_c.Supports(GPU_SUPPORTS_ANISOTROPY) && g_Config.iAnisotropyLevel > 0 && minf == D3DTEXF_LINEAR) {
minf = D3DTEXF_ANISOTROPIC;
}
@ -653,8 +653,11 @@ void TextureCacheDX9::StartFrame() {
Decimate();
}
DWORD anisotropyLevel = (DWORD)g_Config.iAnisotropyLevel > maxAnisotropyLevel ? maxAnisotropyLevel : g_Config.iAnisotropyLevel;
pD3Ddevice->SetSamplerState(0, D3DSAMP_MAXANISOTROPY, anisotropyLevel);
if (gstate_c.Supports(GPU_SUPPORTS_ANISOTROPY)) {
DWORD aniso = 1 << g_Config.iAnisotropyLevel;
DWORD anisotropyLevel = aniso > maxAnisotropyLevel ? maxAnisotropyLevel : aniso;
pD3Ddevice->SetSamplerState(0, D3DSAMP_MAXANISOTROPY, anisotropyLevel);
}
}
@ -1639,6 +1642,7 @@ TextureCacheDX9::TexCacheEntry::Status TextureCacheDX9::CheckAlpha(const u32 *pi
return (TexCacheEntry::Status)res;
}
// TODO: xoffset and yoffset are unused - bug?
static inline void copyTexture(int xoffset, int yoffset, int w, int h, int pitch, int srcfmt, int fmt, void * pSrc, void * pDst) {
int y;
switch(fmt) {

View File

@ -828,11 +828,20 @@ rotateVBO:
SoftwareTransformResult result;
memset(&result, 0, sizeof(result));
SoftwareTransformParams params;
memset(&params, 0, sizeof(params));
params.decoded = decoded;
params.transformed = transformed;
params.transformedExpanded = transformedExpanded;
params.fbman = framebufferManager_;
params.texCache = textureCache_;
params.allowSeparateAlphaClear = true;
int maxIndex = indexGen.MaxIndex();
SoftwareTransform(
prim, decoded, indexGen.VertexCount(),
prim, indexGen.VertexCount(),
dec_->VertexType(), inds, GE_VTYPE_IDX_16BIT, dec_->GetDecVtxFmt(),
maxIndex, framebufferManager_, textureCache_, transformed, transformedExpanded, drawBuffer, numTrans, drawIndexed, &result, 1.0f);
maxIndex, drawBuffer, numTrans, drawIndexed, &params, &result);
ApplyDrawStateLate();
vshader = shaderManager_->ApplyShader(prim, lastVType_);

View File

@ -102,7 +102,6 @@ void GenerateVertexShaderDX9(const ShaderID &id, char *buffer) {
if (enableBones) {
numBoneWeights = 1 + id.Bits(VS_BIT_BONES, 3);
}
int texFmtScale = id.Bits(VS_BIT_TEXCOORD_FMTSCALE, 2);
WRITE(p, "#pragma warning( disable : 3571 )\n");

View File

@ -1987,7 +1987,7 @@ bool FramebufferManager::GetFramebuffer(u32 fb_address, int fb_stride, GEBufferF
return true;
}
buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GE_FORMAT_8888, false, true);
buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GE_FORMAT_8888, !useBufferedRendering_, true);
if (vfb->fbo)
fbo_bind_for_read(vfb->fbo);
if (gl_extensions.GLES3 || !gl_extensions.IsGLES)

View File

@ -565,6 +565,8 @@ void GLES_GPU::CheckGPUFeatures() {
if (gl_extensions.GLES3 || !gl_extensions.IsGLES)
features |= GPU_SUPPORTS_TEXTURE_LOD_CONTROL;
features |= GPU_SUPPORTS_ANISOTROPY;
// If we already have a 16-bit depth buffer, we don't need to round.
if (fbo_standard_z_depth() > 16) {
if (!g_Config.bHighQualityDepth && (features & GPU_SUPPORTS_ACCURATE_DEPTH) != 0) {

View File

@ -152,7 +152,7 @@ inline void TransformDrawEngine::ResetShaderBlending() {
}
void TransformDrawEngine::ApplyDrawState(int prim) {
// TODO: All this setup is soon so expensive that we'll need dirty flags, or simply do it in the command writes where we detect dirty by xoring. Silly to do all this work on every drawcall.
// TODO: All this setup is so expensive that we'll need dirty flags, or simply do it in the command writes where we detect dirty by xoring. Silly to do all this work on every drawcall.
if (gstate_c.textureChanged != TEXCHANGE_UNCHANGED && !gstate.isModeClear() && gstate.isTextureMapEnabled()) {
textureCache_->SetTexture();

View File

@ -1485,9 +1485,11 @@ void TextureCache::SetTexture(bool force) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
}
int aniso = 1 << g_Config.iAnisotropyLevel;
float anisotropyLevel = (float) aniso > maxAnisotropyLevel ? maxAnisotropyLevel : (float) aniso;
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropyLevel);
if (gstate_c.Supports(GPU_SUPPORTS_ANISOTROPY)) {
int aniso = 1 << g_Config.iAnisotropyLevel;
float anisotropyLevel = (float) aniso > maxAnisotropyLevel ? maxAnisotropyLevel : (float) aniso;
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropyLevel);
}
gstate_c.textureFullAlpha = entry->GetAlphaStatus() == TexCacheEntry::STATUS_ALPHA_FULL;
gstate_c.textureSimpleAlpha = entry->GetAlphaStatus() != TexCacheEntry::STATUS_ALPHA_UNKNOWN;

View File

@ -890,11 +890,21 @@ rotateVBO:
SoftwareTransformResult result;
memset(&result, 0, sizeof(result));
// TODO: Keep this static? Faster than repopulating?
SoftwareTransformParams params;
memset(&params, 0, sizeof(params));
params.decoded = decoded;
params.transformed = transformed;
params.transformedExpanded = transformedExpanded;
params.fbman = framebufferManager_;
params.texCache = textureCache_;
params.allowSeparateAlphaClear = true;
int maxIndex = indexGen.MaxIndex();
SoftwareTransform(
prim, decoded, indexGen.VertexCount(),
prim, indexGen.VertexCount(),
dec_->VertexType(), inds, GE_VTYPE_IDX_16BIT, dec_->GetDecVtxFmt(),
maxIndex, framebufferManager_, textureCache_, transformed, transformedExpanded, drawBuffer, numTrans, drawIndexed, &result, 1.0);
maxIndex, drawBuffer, numTrans, drawIndexed, &params, &result);
ApplyDrawStateLate();
LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, lastVType_, prim);

View File

@ -15,11 +15,15 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "Common/GraphicsContext.h"
#include "Core/Core.h"
#include "GPU/GPU.h"
#include "GPU/GPUInterface.h"
#include "GPU/GLES/GLES_GPU.h"
#ifndef NO_VULKAN
#include "GPU/Vulkan/GPU_Vulkan.h"
#endif
#include "GPU/Null/NullGpu.h"
#include "GPU/Software/SoftGpu.h"
@ -41,7 +45,8 @@ static void SetGPU(T *obj) {
#ifdef USE_CRT_DBG
#undef new
#endif
bool GPU_Init(GraphicsContext *ctx) {
bool GPU_Init(GraphicsContext *ctx, Thin3DContext *thin3d) {
switch (PSP_CoreParameter().gpuCore) {
case GPU_NULL:
SetGPU(new NullGPU());
@ -50,13 +55,20 @@ bool GPU_Init(GraphicsContext *ctx) {
SetGPU(new GLES_GPU(ctx));
break;
case GPU_SOFTWARE:
SetGPU(new SoftGPU(ctx));
SetGPU(new SoftGPU(ctx, thin3d));
break;
case GPU_DIRECTX9:
#if defined(_WIN32)
SetGPU(new DIRECTX9_GPU(ctx));
#endif
break;
case GPU_DIRECTX11:
return nullptr;
#ifndef NO_VULKAN
case GPU_VULKAN:
SetGPU(new GPU_Vulkan(ctx));
break;
#endif
}
return gpu != NULL;

View File

@ -89,6 +89,9 @@ struct GPUStatistics {
int vertexGPUCycles;
int otherGPUCycles;
int gpuCommandsAtCallLevel[4];
int pushUBOSpaceUsed;
int pushVertexSpaceUsed;
int pushIndexSpaceUsed;
// Total statistics, updated by the GPU core in UpdateStats
int numVBlanks;
@ -104,5 +107,7 @@ extern GPUStatistics gpuStats;
extern GPUInterface *gpu;
extern GPUDebugInterface *gpuDebug;
bool GPU_Init(GraphicsContext *ctx);
class Thin3DContext;
bool GPU_Init(GraphicsContext *ctx, Thin3DContext *thin3d);
void GPU_Shutdown();

View File

@ -45,7 +45,6 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>false</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v140_xp</PlatformToolset>
</PropertyGroup>
@ -84,7 +83,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<AdditionalIncludeDirectories>../common;..;../ext/native;../ext/native/ext/glew;</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../common;..;../ext;../ext/native;../ext/native/ext/glew;</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
@ -106,7 +105,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<AdditionalIncludeDirectories>../common;..;../ext/native;../ext/native/ext/glew;</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../common;..;../ext;../ext/native;../ext/native/ext/glew;</AdditionalIncludeDirectories>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<OmitFramePointers>false</OmitFramePointers>
@ -132,7 +131,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>../common;..;../ext/native;../ext/native/ext/glew;</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../common;..;../ext;../ext/native;../ext/native/ext/glew;</AdditionalIncludeDirectories>
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
@ -158,7 +157,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>../common;..;../ext/native;../ext/native/ext/glew;</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../common;..;../ext;../ext/native;../ext/native/ext/glew;</AdditionalIncludeDirectories>
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
@ -168,6 +167,7 @@
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PreprocessorDefinitions>USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_M_X64=1;_LIB;NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StringPooling>true</StringPooling>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -244,6 +244,18 @@
<ClInclude Include="Software\SoftGpu.h" />
<ClInclude Include="Software\TransformUnit.h" />
<ClInclude Include="Common\TextureDecoder.h" />
<ClInclude Include="Vulkan\DepalettizeShaderVulkan.h" />
<ClInclude Include="Vulkan\DrawEngineVulkan.h" />
<ClInclude Include="Vulkan\FragmentShaderGeneratorVulkan.h" />
<ClInclude Include="Vulkan\FramebufferVulkan.h" />
<ClInclude Include="Vulkan\GPU_Vulkan.h" />
<ClInclude Include="Vulkan\PipelineManagerVulkan.h" />
<ClInclude Include="Vulkan\ShaderManagerVulkan.h" />
<ClInclude Include="Vulkan\StateMappingVulkan.h" />
<ClInclude Include="Vulkan\TextureCacheVulkan.h" />
<ClInclude Include="Vulkan\TextureScalerVulkan.h" />
<ClInclude Include="Vulkan\VertexShaderGeneratorVulkan.h" />
<ClInclude Include="Vulkan\VulkanUtil.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\ext\xbrz\xbrz.cpp" />
@ -322,6 +334,18 @@
<ClCompile Include="Software\SoftGpu.cpp" />
<ClCompile Include="Software\TransformUnit.cpp" />
<ClCompile Include="Common\TextureDecoder.cpp" />
<ClCompile Include="Vulkan\DepalettizeShaderVulkan.cpp" />
<ClCompile Include="Vulkan\DrawEngineVulkan.cpp" />
<ClCompile Include="Vulkan\FragmentShaderGeneratorVulkan.cpp" />
<ClCompile Include="Vulkan\FramebufferVulkan.cpp" />
<ClCompile Include="Vulkan\GPU_Vulkan.cpp" />
<ClCompile Include="Vulkan\PipelineManagerVulkan.cpp" />
<ClCompile Include="Vulkan\ShaderManagerVulkan.cpp" />
<ClCompile Include="Vulkan\StateMappingVulkan.cpp" />
<ClCompile Include="Vulkan\TextureCacheVulkan.cpp" />
<ClCompile Include="Vulkan\TextureScalerVulkan.cpp" />
<ClCompile Include="Vulkan\VertexShaderGeneratorVulkan.cpp" />
<ClCompile Include="Vulkan\VulkanUtil.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Common\Common.vcxproj">

View File

@ -22,6 +22,9 @@
<Filter Include="Debugger">
<UniqueIdentifier>{0cbddc00-4aa3-41d0-bed2-a454d37f838e}</UniqueIdentifier>
</Filter>
<Filter Include="Vulkan">
<UniqueIdentifier>{3c621896-140c-4c8b-8e4d-a478bfdeca8a}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ge_constants.h">
@ -201,6 +204,42 @@
<ClInclude Include="Common\ShaderId.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="Vulkan\DepalettizeShaderVulkan.h">
<Filter>Vulkan</Filter>
</ClInclude>
<ClInclude Include="Vulkan\DrawEngineVulkan.h">
<Filter>Vulkan</Filter>
</ClInclude>
<ClInclude Include="Vulkan\FragmentShaderGeneratorVulkan.h">
<Filter>Vulkan</Filter>
</ClInclude>
<ClInclude Include="Vulkan\FramebufferVulkan.h">
<Filter>Vulkan</Filter>
</ClInclude>
<ClInclude Include="Vulkan\GPU_Vulkan.h">
<Filter>Vulkan</Filter>
</ClInclude>
<ClInclude Include="Vulkan\PipelineManagerVulkan.h">
<Filter>Vulkan</Filter>
</ClInclude>
<ClInclude Include="Vulkan\ShaderManagerVulkan.h">
<Filter>Vulkan</Filter>
</ClInclude>
<ClInclude Include="Vulkan\StateMappingVulkan.h">
<Filter>Vulkan</Filter>
</ClInclude>
<ClInclude Include="Vulkan\TextureCacheVulkan.h">
<Filter>Vulkan</Filter>
</ClInclude>
<ClInclude Include="Vulkan\TextureScalerVulkan.h">
<Filter>Vulkan</Filter>
</ClInclude>
<ClInclude Include="Vulkan\VertexShaderGeneratorVulkan.h">
<Filter>Vulkan</Filter>
</ClInclude>
<ClInclude Include="Vulkan\VulkanUtil.h">
<Filter>Vulkan</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Math3D.cpp">
@ -386,5 +425,39 @@
<ClCompile Include="Common\ShaderId.cpp">
<Filter>Common</Filter>
</ClCompile>
<ClCompile Include="Vulkan\DepalettizeShaderVulkan.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\DrawEngineVulkan.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\FragmentShaderGeneratorVulkan.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\FramebufferVulkan.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\GPU_Vulkan.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\PipelineManagerVulkan.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\ShaderManagerVulkan.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\StateMappingVulkan.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\TextureCacheVulkan.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\TextureScalerVulkan.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\VertexShaderGeneratorVulkan.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\VulkanUtil.cpp" />
</ItemGroup>
</Project>

View File

@ -456,6 +456,9 @@ enum {
GPU_SUPPORTS_BLEND_MINMAX = FLAG_BIT(4),
GPU_SUPPORTS_LOGIC_OP = FLAG_BIT(5),
GPU_USE_DEPTH_RANGE_HACK = FLAG_BIT(6),
GPU_SUPPORTS_WIDE_LINES = FLAG_BIT(7),
GPU_SUPPORTS_ANISOTROPY = FLAG_BIT(8),
GPU_SUPPORTS_LARGE_VIEWPORTS = FLAG_BIT(16),
GPU_SUPPORTS_ACCURATE_DEPTH = FLAG_BIT(17),
GPU_SUPPORTS_VAO = FLAG_BIT(18),
GPU_SUPPORTS_ANY_COPY_IMAGE = FLAG_BIT(19),

View File

@ -42,18 +42,14 @@ FormatBuffer fb;
FormatBuffer depthbuf;
u32 clut[4096];
static Thin3DContext *thin3d = nullptr;
static Thin3DTexture *fbTex = nullptr;
static Thin3DVertexFormat *vformat = nullptr;
static Thin3DDepthStencilState *depth = nullptr;
static Thin3DBuffer *vdata = nullptr;
static Thin3DBuffer *idata = nullptr;
static std::vector<u32> fbTexBuffer;
SoftGPU::SoftGPU(GraphicsContext *gfxCtx)
: gfxCtx_(gfxCtx)
SoftGPU::SoftGPU(GraphicsContext *gfxCtx, Thin3DContext *_thin3D)
: gfxCtx_(gfxCtx), thin3d(_thin3D)
{
thin3d = gfxCtx_->CreateThin3DContext();
fbTex = thin3d->CreateTexture(LINEAR2D, RGBA8888, 480, 272, 1, 1);
std::vector<Thin3DVertexComponent> components;
@ -87,8 +83,6 @@ SoftGPU::~SoftGPU() {
vformat = nullptr;
fbTex->Release();
fbTex = nullptr;
thin3d->Release();
thin3d = nullptr;
}
void SoftGPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) {

View File

@ -45,10 +45,12 @@ typedef struct {
} FormatBuffer;
class ShaderManager;
class Thin3DContext;
class Thin3DTexture;
class SoftGPU : public GPUCommon {
public:
SoftGPU(GraphicsContext *gfxCtx);
SoftGPU(GraphicsContext *gfxCtx, Thin3DContext *_thin3D);
~SoftGPU();
void InitClear() override {}
void ExecuteOp(u32 op, u32 diff) override;
@ -102,4 +104,7 @@ private:
GEBufferFormat displayFormat_;
GraphicsContext *gfxCtx_;
Thin3DTexture *fbTex;
Thin3DContext *thin3d;
std::vector<u32> fbTexBuffer;
};

View File

@ -0,0 +1,47 @@
// Copyright (c) 2014- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY{} without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "GPU/Vulkan/DepalettizeShaderVulkan.h"
DepalShaderCacheVulkan::DepalShaderCacheVulkan() {
}
DepalShaderCacheVulkan::~DepalShaderCacheVulkan() {
}
DepalShaderVulkan *DepalShaderCacheVulkan::GetDepalettizeShader(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat) {
return nullptr;
}
VulkanTexture *DepalShaderCacheVulkan::GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, u32 *rawClut) {
return nullptr;
}
void DepalShaderCacheVulkan::Clear() {}
void DepalShaderCacheVulkan::Decimate() {}
u32 DepalShaderCacheVulkan::GenerateShaderID(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat) {
return 0;
}
bool DepalShaderCacheVulkan::CreateVertexShader() {
return false;
}

View File

@ -0,0 +1,64 @@
// Copyright (c) 2014- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include <map>
#include "Common/CommonTypes.h"
#include "GPU/ge_constants.h"
class DepalShaderVulkan {
public:
/*
GLuint program;
GLuint fragShader;
GLint a_position;
GLint a_texcoord0;
*/
};
class DepalTextureVulkan {
public:
int texture;
int lastFrame;
};
class VulkanTexture;
// Caches both shaders and palette textures.
// Could even avoid bothering with palette texture and just use uniform data...
class DepalShaderCacheVulkan {
public:
DepalShaderCacheVulkan();
~DepalShaderCacheVulkan();
// This also uploads the palette and binds the correct texture.
DepalShaderVulkan *GetDepalettizeShader(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat);
VulkanTexture *GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, u32 *rawClut);
void Clear();
void Decimate();
private:
u32 GenerateShaderID(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat);
bool CreateVertexShader();
// GLuint vertexShader_;
std::map<u32, DepalShaderVulkan *> cache_;
std::map<u32, DepalTextureVulkan *> texCache_;
};

View File

@ -0,0 +1,813 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <cassert>
#include "base/logging.h"
#include "base/timeutil.h"
#include "math/dataconv.h"
#include "Common/MemoryUtil.h"
#include "Core/MemMap.h"
#include "Core/Host.h"
#include "Core/System.h"
#include "Core/Reporting.h"
#include "Core/Config.h"
#include "Core/CoreTiming.h"
#include "GPU/Math3D.h"
#include "GPU/GPUState.h"
#include "GPU/ge_constants.h"
#include "Common/Vulkan/VulkanContext.h"
#include "Common/Vulkan/VulkanMemory.h"
#include "GPU/Common/TextureDecoder.h"
#include "GPU/Common/SplineCommon.h"
#include "GPU/Common/TransformCommon.h"
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/SoftwareTransformCommon.h"
#include "GPU/Common/DrawEngineCommon.h"
#include "GPU/Vulkan/DrawEngineVulkan.h"
#include "GPU/Vulkan/TextureCacheVulkan.h"
#include "GPU/Vulkan/ShaderManagerVulkan.h"
#include "GPU/Vulkan/PipelineManagerVulkan.h"
#include "GPU/Vulkan/FramebufferVulkan.h"
#include "GPU/Vulkan/GPU_Vulkan.h"
enum {
DRAW_BINDING_TEXTURE = 0,
DRAW_BINDING_2ND_TEXTURE = 1,
DRAW_BINDING_DYNUBO_BASE = 2,
DRAW_BINDING_DYNUBO_LIGHT = 3,
DRAW_BINDING_DYNUBO_BONE = 4,
};
enum {
TRANSFORMED_VERTEX_BUFFER_SIZE = VERTEX_BUFFER_MAX * sizeof(TransformedVertex)
};
DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan)
:
vulkan_(vulkan),
prevPrim_(GE_PRIM_INVALID),
lastVTypeID_(-1),
pipelineManager_(nullptr),
textureCache_(nullptr),
framebufferManager_(nullptr),
numDrawCalls(0),
vertexCountInDrawCalls(0),
fboTexNeedBind_(false),
fboTexBound_(false),
curFrame_(0) {
memset(&decOptions_, 0, sizeof(decOptions_));
decOptions_.expandAllUVtoFloat = false; // this may be a good idea though.
decOptions_.expandAllWeightsToFloat = false;
decOptions_.expand8BitNormalsToFloat = false;
// Allocate nicely aligned memory. Maybe graphics drivers will
// appreciate it.
// All this is a LOT of memory, need to see if we can cut down somehow.
decoded = (u8 *)AllocateMemoryPages(DECODED_VERTEX_BUFFER_SIZE);
decIndex = (u16 *)AllocateMemoryPages(DECODED_INDEX_BUFFER_SIZE);
splineBuffer = (u8 *)AllocateMemoryPages(SPLINE_BUFFER_SIZE);
transformed = (TransformedVertex *)AllocateMemoryPages(TRANSFORMED_VERTEX_BUFFER_SIZE);
transformedExpanded = (TransformedVertex *)AllocateMemoryPages(3 * TRANSFORMED_VERTEX_BUFFER_SIZE);
indexGen.Setup(decIndex);
// All resources we need for PSP drawing. Usually only bindings 0 and 2-4 are populated.
VkDescriptorSetLayoutBinding bindings[5];
bindings[0].descriptorCount = 1;
bindings[0].pImmutableSamplers = nullptr;
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[0].binding = DRAW_BINDING_TEXTURE;
bindings[1].descriptorCount = 1;
bindings[1].pImmutableSamplers = nullptr;
bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[1].binding = DRAW_BINDING_2ND_TEXTURE;
bindings[2].descriptorCount = 1;
bindings[2].pImmutableSamplers = nullptr;
bindings[2].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
bindings[2].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[2].binding = DRAW_BINDING_DYNUBO_BASE;
bindings[3].descriptorCount = 1;
bindings[3].pImmutableSamplers = nullptr;
bindings[3].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
bindings[3].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[3].binding = DRAW_BINDING_DYNUBO_LIGHT;
bindings[4].descriptorCount = 1;
bindings[4].pImmutableSamplers = nullptr;
bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
bindings[4].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[4].binding = DRAW_BINDING_DYNUBO_BONE;
VkDevice device = vulkan_->GetDevice();
VkDescriptorSetLayoutCreateInfo dsl;
dsl.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
dsl.pNext = nullptr;
dsl.bindingCount = 5;
dsl.pBindings = bindings;
VkResult res = vkCreateDescriptorSetLayout(device, &dsl, nullptr, &descriptorSetLayout_);
assert(VK_SUCCESS == res);
VkDescriptorPoolSize dpTypes[2];
dpTypes[0].descriptorCount = 2048;
dpTypes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
dpTypes[1].descriptorCount = 200;
dpTypes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
VkDescriptorPoolCreateInfo dp;
dp.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
dp.pNext = nullptr;
dp.flags = 0; // Don't want to mess around with individually freeing these, let's go fixed each frame and zap the whole array. Might try the dynamic approach later.
dp.maxSets = 1000;
dp.pPoolSizes = dpTypes;
dp.poolSizeCount = ARRAY_SIZE(dpTypes);
// We are going to use one-shot descriptors in the initial implementation. Might look into caching them
// if creating and updating them turns out to be expensive.
for (int i = 0; i < 2; i++) {
VkResult res = vkCreateDescriptorPool(vulkan_->GetDevice(), &dp, nullptr, &frame_[i].descPool);
assert(VK_SUCCESS == res);
frame_[i].pushUBO = new VulkanPushBuffer(vulkan_, 16 * 1024 * 1024); // TODO: Do something more dynamic
frame_[i].pushVertex = new VulkanPushBuffer(vulkan_, 8 * 1024 * 1024); // TODO: Do something more dynamic
frame_[i].pushIndex = new VulkanPushBuffer(vulkan_, 2 * 1024 * 1024); // TODO: Do something more dynamic
}
VkPipelineLayoutCreateInfo pl;
pl.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pl.pNext = nullptr;
pl.pPushConstantRanges = nullptr;
pl.pushConstantRangeCount = 0;
pl.setLayoutCount = 1;
pl.pSetLayouts = &descriptorSetLayout_;
pl.flags = 0;
res = vkCreatePipelineLayout(device, &pl, nullptr, &pipelineLayout_);
assert(VK_SUCCESS == res);
VkSamplerCreateInfo samp = {};
samp.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samp.pNext = nullptr;
samp.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samp.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samp.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samp.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
samp.flags = 0;
samp.magFilter = VK_FILTER_LINEAR;
samp.minFilter = VK_FILTER_LINEAR;
res = vkCreateSampler(device, &samp, nullptr, &depalSampler_);
assert(VK_SUCCESS == res);
}
DrawEngineVulkan::~DrawEngineVulkan() {
FreeMemoryPages(decoded, DECODED_VERTEX_BUFFER_SIZE);
FreeMemoryPages(decIndex, DECODED_INDEX_BUFFER_SIZE);
FreeMemoryPages(splineBuffer, SPLINE_BUFFER_SIZE);
FreeMemoryPages(transformed, TRANSFORMED_VERTEX_BUFFER_SIZE);
FreeMemoryPages(transformedExpanded, 3 * TRANSFORMED_VERTEX_BUFFER_SIZE);
for (int i = 0; i < 2; i++) {
vulkan_->Delete().QueueDeleteDescriptorPool(frame_[i].descPool);
frame_[i].pushUBO->Destroy(vulkan_);
frame_[i].pushVertex->Destroy(vulkan_);
frame_[i].pushIndex->Destroy(vulkan_);
delete frame_[i].pushUBO;
delete frame_[i].pushVertex;
delete frame_[i].pushIndex;
}
vulkan_->Delete().QueueDeleteSampler(depalSampler_);
}
void DrawEngineVulkan::BeginFrame() {
FrameData *frame = &frame_[curFrame_ & 1];
vkResetDescriptorPool(vulkan_->GetDevice(), frame->descPool, 0);
frame->descSets.clear();
// First reset all buffers, then begin. This is so that Reset can free memory and Begin can allocate it,
// if growing the buffer is needed. Doing it this way will reduce fragmentation if more than one buffer
// needs to grow in the same frame. The state where many buffers are reset can also be used to
// defragment memory.
frame->pushUBO->Reset();
frame->pushVertex->Reset();
frame->pushIndex->Reset();
frame->pushUBO->Begin(vulkan_->GetDevice());
frame->pushVertex->Begin(vulkan_->GetDevice());
frame->pushIndex->Begin(vulkan_->GetDevice());
DirtyAllUBOs();
}
void DrawEngineVulkan::EndFrame() {
FrameData *frame = &frame_[curFrame_ & 1];
gpuStats.pushUBOSpaceUsed = (int)frame->pushUBO->GetOffset();
gpuStats.pushVertexSpaceUsed = (int)frame->pushVertex->GetOffset();
gpuStats.pushIndexSpaceUsed = (int)frame->pushIndex->GetOffset();
frame->pushUBO->End(vulkan_->GetDevice());
frame->pushVertex->End(vulkan_->GetDevice());
frame->pushIndex->End(vulkan_->GetDevice());
curFrame_++;
}
VertexDecoder *DrawEngineVulkan::GetVertexDecoder(u32 vtype) {
auto iter = decoderMap_.find(vtype);
if (iter != decoderMap_.end())
return iter->second;
VertexDecoder *dec = new VertexDecoder();
dec->SetVertexType(vtype, decOptions_, decJitCache_);
decoderMap_[vtype] = dec;
return dec;
}
void DrawEngineVulkan::SetupVertexDecoder(u32 vertType) {
SetupVertexDecoderInternal(vertType);
}
inline void DrawEngineVulkan::SetupVertexDecoderInternal(u32 vertType) {
// As the decoder depends on the UVGenMode when we use UV prescale, we simply mash it
// into the top of the verttype where there are unused bits.
const u32 vertTypeID = (vertType & 0xFFFFFF) | (gstate.getUVGenMode() << 24);
// If vtype has changed, setup the vertex decoder.
if (vertTypeID != lastVTypeID_) {
dec_ = GetVertexDecoder(vertTypeID);
lastVTypeID_ = vertTypeID;
}
}
void DrawEngineVulkan::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) {
if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls >= MAX_DEFERRED_DRAW_CALLS || vertexCountInDrawCalls + vertexCount > VERTEX_BUFFER_MAX)
Flush(cmd_);
// TODO: Is this the right thing to do?
if (prim == GE_PRIM_KEEP_PREVIOUS) {
prim = prevPrim_ != GE_PRIM_INVALID ? prevPrim_ : GE_PRIM_POINTS;
} else {
prevPrim_ = prim;
}
SetupVertexDecoderInternal(vertType);
*bytesRead = vertexCount * dec_->VertexSize();
if ((vertexCount < 2 && prim > 0) || (vertexCount < 3 && prim > 2 && prim != GE_PRIM_RECTANGLES))
return;
DeferredDrawCall &dc = drawCalls[numDrawCalls];
dc.verts = verts;
dc.inds = inds;
dc.vertType = vertType;
dc.indexType = (vertType & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT;
dc.prim = prim;
dc.vertexCount = vertexCount;
if (inds) {
GetIndexBounds(inds, vertexCount, vertType, &dc.indexLowerBound, &dc.indexUpperBound);
} else {
dc.indexLowerBound = 0;
dc.indexUpperBound = vertexCount - 1;
}
numDrawCalls++;
vertexCountInDrawCalls += vertexCount;
if (prim == GE_PRIM_RECTANGLES && (gstate.getTextureAddress(0) & 0x3FFFFFFF) == (gstate.getFrameBufAddress() & 0x3FFFFFFF)) {
// Rendertarget == texture?
if (!g_Config.bDisableSlowFramebufEffects) {
gstate_c.textureChanged |= TEXCHANGE_PARAMSONLY;
Flush(cmd_);
}
}
}
void DrawEngineVulkan::DecodeVerts(VulkanPushBuffer *push, uint32_t *bindOffset, VkBuffer *vkbuf) {
int decodedVerts = 0;
u8 *dest = decoded;
// Figure out how much pushbuffer space we need to allocate.
if (push) {
int vertsToDecode = 0;
for (int i = 0; i < numDrawCalls; i++) {
const DeferredDrawCall &dc = drawCalls[i];
if (dc.indexType == GE_VTYPE_IDX_NONE >> GE_VTYPE_IDX_SHIFT) {
vertsToDecode += dc.indexUpperBound - dc.indexLowerBound + 1;
} else {
vertsToDecode += dc.vertexCount;
}
}
dest = (u8 *)push->Push(vertsToDecode * dec_->GetDecVtxFmt().stride, bindOffset, vkbuf);
}
for (int i = 0; i < numDrawCalls; i++) {
const DeferredDrawCall &dc = drawCalls[i];
indexGen.SetIndex(decodedVerts);
int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound;
void *inds = dc.inds;
if (dc.indexType == GE_VTYPE_IDX_NONE >> GE_VTYPE_IDX_SHIFT) {
// Decode the verts and apply morphing. Simple.
dec_->DecodeVerts(dest + decodedVerts * (int)dec_->GetDecVtxFmt().stride,
dc.verts, indexLowerBound, indexUpperBound);
decodedVerts += indexUpperBound - indexLowerBound + 1;
indexGen.AddPrim(dc.prim, dc.vertexCount);
} else {
// It's fairly common that games issue long sequences of PRIM calls, with differing
// inds pointer but the same base vertex pointer. We'd like to reuse vertices between
// these as much as possible, so we make sure here to combine as many as possible
// into one nice big drawcall, sharing data.
// 1. Look ahead to find the max index, only looking as "matching" drawcalls.
// Expand the lower and upper bounds as we go.
int lastMatch = i;
const int total = numDrawCalls;
for (int j = i + 1; j < total; ++j) {
if (drawCalls[j].verts != dc.verts)
break;
indexLowerBound = std::min(indexLowerBound, (int)drawCalls[j].indexLowerBound);
indexUpperBound = std::max(indexUpperBound, (int)drawCalls[j].indexUpperBound);
lastMatch = j;
}
// 2. Loop through the drawcalls, translating indices as we go.
switch (dc.indexType) {
case GE_VTYPE_IDX_8BIT >> GE_VTYPE_IDX_SHIFT:
for (int j = i; j <= lastMatch; j++) {
indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u8 *)drawCalls[j].inds, indexLowerBound);
}
break;
case GE_VTYPE_IDX_16BIT >> GE_VTYPE_IDX_SHIFT:
for (int j = i; j <= lastMatch; j++) {
indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u16 *)drawCalls[j].inds, indexLowerBound);
}
break;
}
const int vertexCount = indexUpperBound - indexLowerBound + 1;
// This check is a workaround for Pangya Fantasy Golf, which sends bogus index data when switching items in "My Room" sometimes.
if (decodedVerts + vertexCount > VERTEX_BUFFER_MAX) {
return;
}
// 3. Decode that range of vertex data.
dec_->DecodeVerts(dest + decodedVerts * (int)dec_->GetDecVtxFmt().stride,
dc.verts, indexLowerBound, indexUpperBound);
decodedVerts += vertexCount;
// 4. Advance indexgen vertex counter.
indexGen.Advance(vertexCount);
i = lastMatch;
}
}
// Sanity check
if (indexGen.Prim() < 0) {
ERROR_LOG_REPORT(G3D, "DecodeVerts: Failed to deduce prim: %i", indexGen.Prim());
// Force to points (0)
indexGen.AddPrim(GE_PRIM_POINTS, 0);
}
}
inline u32 ComputeMiniHashRange(const void *ptr, size_t sz) {
// Switch to u32 units.
const u32 *p = (const u32 *)ptr;
sz >>= 2;
if (sz > 100) {
size_t step = sz / 4;
u32 hash = 0;
for (size_t i = 0; i < sz; i += step) {
hash += DoReliableHash32(p + i, 100, 0x3A44B9C4);
}
return hash;
} else {
return p[0] + p[sz - 1];
}
}
VkDescriptorSet DrawEngineVulkan::GetDescriptorSet(VkImageView imageView, VkSampler sampler, VkBuffer base, VkBuffer light, VkBuffer bone) {
DescriptorSetKey key;
key.imageView_ = imageView;
key.sampler_ = sampler;
key.secondaryImageView_ = VK_NULL_HANDLE;
key.base_ = base;
key.light_ = light;
key.bone_ = bone;
assert(base != VK_NULL_HANDLE);
assert(light != VK_NULL_HANDLE);
assert(bone != VK_NULL_HANDLE);
FrameData *frame = &frame_[curFrame_ & 1];
auto iter = frame->descSets.find(key);
if (iter != frame->descSets.end()) {
return iter->second;
}
// Didn't find one in the frame descriptor set cache, let's make a new one.
// We wipe the cache on every frame.
VkDescriptorSet desc;
VkDescriptorSetAllocateInfo descAlloc;
VkDescriptorImageInfo tex;
descAlloc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
descAlloc.pNext = nullptr;
descAlloc.pSetLayouts = &descriptorSetLayout_;
descAlloc.descriptorPool = frame->descPool;
descAlloc.descriptorSetCount = 1;
VkResult result = vkAllocateDescriptorSets(vulkan_->GetDevice(), &descAlloc, &desc);
assert(result == VK_SUCCESS);
// We just don't write to the slots we don't care about.
VkWriteDescriptorSet writes[4];
memset(writes, 0, sizeof(writes));
// Main texture
int n = 0;
if (imageView) {
// TODO: Also support LAYOUT_GENERAL to be able to texture from framebuffers without transitioning them?
tex.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
tex.imageView = imageView;
tex.sampler = sampler;
writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writes[n].pNext = nullptr;
writes[n].dstBinding = DRAW_BINDING_TEXTURE;
writes[n].pImageInfo = &tex;
writes[n].descriptorCount = 1;
writes[n].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writes[n].dstSet = desc;
n++;
}
// Skipping 2nd texture for now.
// Uniform buffer objects
VkDescriptorBufferInfo buf[3];
int count = 0;
buf[count].buffer = base;
buf[count].offset = 0;
buf[count].range = sizeof(UB_VS_FS_Base);
count++;
buf[count].buffer = light;
buf[count].offset = 0;
buf[count].range = sizeof(UB_VS_Lights);
count++;
buf[count].buffer = bone;
buf[count].offset = 0;
buf[count].range = sizeof(UB_VS_Bones);
count++;
for (int i = 0; i < count; i++) {
writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writes[n].pNext = nullptr;
writes[n].dstBinding = DRAW_BINDING_DYNUBO_BASE + i;
writes[n].dstArrayElement = 0;
writes[n].pBufferInfo = &buf[i];
writes[n].dstSet = desc;
writes[n].descriptorCount = 1;
writes[n].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
n++;
}
vkUpdateDescriptorSets(vulkan_->GetDevice(), n, writes, 0, nullptr);
frame->descSets[key] = desc;
return desc;
}
void DrawEngineVulkan::DirtyAllUBOs() {
baseUBOOffset = 0;
lightUBOOffset = 0;
boneUBOOffset = 0;
baseBuf = VK_NULL_HANDLE;
lightBuf = VK_NULL_HANDLE;
boneBuf = VK_NULL_HANDLE;
dirtyUniforms_ = DIRTY_BASE_UNIFORMS | DIRTY_LIGHT_UNIFORMS | DIRTY_BONE_UNIFORMS;
}
// The inline wrapper in the header checks for numDrawCalls == 0d
void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
gpuStats.numFlushes++;
FrameData *frame = &frame_[curFrame_ & 1];
// Note than when we implement overflow in pushbuffer, we need to make sure to overflow here, not between
// the three ubo pushes. The reason is that the three UBOs must be in the same buffer as that's how we
// designed the descriptor set.
VkImageView imageView = VK_NULL_HANDLE;
VkSampler sampler = VK_NULL_HANDLE;
// TODO: The descriptor set seems to be unbinding the texture when not specified. Cache it or the imageView instead?
// TODO: Add this back when fixed: gstate_c.textureChanged != TEXCHANGE_UNCHANGED &&
if (!gstate.isModeClear() && gstate.isTextureMapEnabled()) {
textureCache_->SetTexture(frame->pushUBO);
gstate_c.textureChanged = TEXCHANGE_UNCHANGED;
if (gstate_c.needShaderTexClamp) {
// We will rarely need to set this, so let's do it every time on use rather than in runloop.
// Most of the time non-framebuffer textures will be used which can be clamped themselves.
shaderManager_->DirtyUniform(DIRTY_TEXCLAMP);
}
textureCache_->ApplyTexture(imageView, sampler);
}
GEPrimitiveType prim = prevPrim_;
bool useHWTransform = CanUseHardwareTransform(prim);
VulkanVertexShader *vshader = nullptr;
VulkanFragmentShader *fshader = nullptr;
uint32_t ibOffset = 0;
uint32_t vbOffset = 0;
if (useHWTransform) {
// We don't detect clears in this path, so here we can switch framebuffers if necessary.
int vertexCount = 0;
int maxIndex = 0;
bool useElements = true;
// Decode directly into the pushbuffer
VkBuffer vbuf;
DecodeVerts(frame->pushVertex, &vbOffset, &vbuf);
gpuStats.numUncachedVertsDrawn += indexGen.VertexCount();
useElements = !indexGen.SeenOnlyPurePrims();
vertexCount = indexGen.VertexCount();
maxIndex = indexGen.MaxIndex();
if (!useElements && indexGen.PureCount()) {
vertexCount = indexGen.PureCount();
}
prim = indexGen.Prim();
bool hasColor = (lastVTypeID_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
if (gstate.isModeThrough()) {
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255);
} else {
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255);
}
VulkanPipelineRasterStateKey pipelineKey;
VulkanDynamicState dynState;
ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey, dynState);
// TODO: Dirty-flag these.
vkCmdSetScissor(cmd_, 0, 1, &dynState.scissor);
vkCmdSetViewport(cmd_, 0, 1, &dynState.viewport);
vkCmdSetStencilReference(cmd_, VK_STENCIL_FRONT_AND_BACK, dynState.stencilRef);
vkCmdSetStencilWriteMask(cmd_, VK_STENCIL_FRONT_AND_BACK, dynState.stencilWriteMask);
vkCmdSetStencilCompareMask(cmd_, VK_STENCIL_FRONT_AND_BACK, dynState.stencilCompareMask);
float bc[4];
Uint8x4ToFloat4(bc, dynState.blendColor);
vkCmdSetBlendConstants(cmd_, bc);
dirtyUniforms_ |= shaderManager_->UpdateUniforms();
shaderManager_->GetShaders(prim, lastVTypeID_, &vshader, &fshader, useHWTransform);
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, pipelineKey, dec_, vshader, fshader, true);
if (!pipeline) {
// Already logged, let's bail out.
return;
}
vkCmdBindPipeline(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); // TODO: Avoid if same as last draw.
if ((dirtyUniforms_ & DIRTY_BASE_UNIFORMS) || baseBuf == VK_NULL_HANDLE) {
baseUBOOffset = shaderManager_->PushBaseBuffer(frame->pushUBO, &baseBuf);
dirtyUniforms_ &= ~DIRTY_BASE_UNIFORMS;
}
if ((dirtyUniforms_ & DIRTY_LIGHT_UNIFORMS) || lightBuf == VK_NULL_HANDLE) {
lightUBOOffset = shaderManager_->PushLightBuffer(frame->pushUBO, &lightBuf);
dirtyUniforms_ &= ~DIRTY_LIGHT_UNIFORMS;
}
if ((dirtyUniforms_ & DIRTY_BONE_UNIFORMS) || boneBuf == VK_NULL_HANDLE) {
boneUBOOffset = shaderManager_->PushBoneBuffer(frame->pushUBO, &boneBuf);
dirtyUniforms_ &= ~DIRTY_BONE_UNIFORMS;
}
VkDescriptorSet ds = GetDescriptorSet(imageView, sampler, baseBuf, lightBuf, boneBuf);
const uint32_t dynamicUBOOffsets[3] = {
baseUBOOffset, lightUBOOffset, boneUBOOffset,
};
vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets);
int stride = dec_->GetDecVtxFmt().stride;
VkDeviceSize offsets[1] = { vbOffset };
if (useElements) {
VkBuffer ibuf;
ibOffset = (uint32_t)frame->pushIndex->Push(decIndex, 2 * indexGen.VertexCount(), &ibuf);
// TODO: Avoid rebinding vertex/index buffers if the vertex size stays the same by using the offset arguments
vkCmdBindVertexBuffers(cmd_, 0, 1, &vbuf, offsets);
vkCmdBindIndexBuffer(cmd_, ibuf, ibOffset, VK_INDEX_TYPE_UINT16);
vkCmdDrawIndexed(cmd_, vertexCount, 1, 0, 0, 0);
} else {
vkCmdBindVertexBuffers(cmd_, 0, 1, &vbuf, offsets);
vkCmdDraw(cmd_, vertexCount, 1, 0, 0);
}
} else {
// Decode to "decoded"
DecodeVerts(nullptr, nullptr, nullptr);
bool hasColor = (lastVTypeID_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
if (gstate.isModeThrough()) {
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255);
} else {
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255);
}
gpuStats.numUncachedVertsDrawn += indexGen.VertexCount();
prim = indexGen.Prim();
// Undo the strip optimization, not supported by the SW code yet.
if (prim == GE_PRIM_TRIANGLE_STRIP)
prim = GE_PRIM_TRIANGLES;
VERBOSE_LOG(G3D, "Flush prim %i SW! %i verts in one go", prim, indexGen.VertexCount());
int numTrans = 0;
bool drawIndexed = false;
u16 *inds = decIndex;
TransformedVertex *drawBuffer = NULL;
SoftwareTransformResult result;
memset(&result, 0, sizeof(result));
SoftwareTransformParams params;
memset(&params, 0, sizeof(params));
params.decoded = decoded;
params.transformed = transformed;
params.transformedExpanded = transformedExpanded;
params.fbman = framebufferManager_;
params.texCache = textureCache_;
params.allowSeparateAlphaClear = false;
int maxIndex = indexGen.MaxIndex();
SoftwareTransform(
prim, indexGen.VertexCount(),
dec_->VertexType(), inds, GE_VTYPE_IDX_16BIT, dec_->GetDecVtxFmt(),
maxIndex, drawBuffer, numTrans, drawIndexed, &params, &result);
// Only here, where we know whether to clear or to draw primitives, should we actually set the current framebuffer! Because that gives use the opportunity
// to use a "pre-clear" render pass, for high efficiency on tilers.
if (result.action == SW_DRAW_PRIMITIVES) {
VulkanPipelineRasterStateKey pipelineKey;
VulkanDynamicState dynState;
ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey, dynState);
// TODO: Dirty-flag these.
vkCmdSetScissor(cmd_, 0, 1, &dynState.scissor);
vkCmdSetViewport(cmd_, 0, 1, &dynState.viewport);
if (dynState.useStencil) {
vkCmdSetStencilWriteMask(cmd_, VK_STENCIL_FRONT_AND_BACK, dynState.stencilWriteMask);
vkCmdSetStencilCompareMask(cmd_, VK_STENCIL_FRONT_AND_BACK, dynState.stencilCompareMask);
}
if (result.setStencil) {
vkCmdSetStencilReference(cmd_, VK_STENCIL_FRONT_AND_BACK, result.stencilValue);
} else if (dynState.useStencil) {
vkCmdSetStencilReference(cmd_, VK_STENCIL_FRONT_AND_BACK, dynState.stencilRef);
}
float bc[4];
Uint8x4ToFloat4(bc, dynState.blendColor);
vkCmdSetBlendConstants(cmd_, bc);
dirtyUniforms_ |= shaderManager_->UpdateUniforms();
shaderManager_->GetShaders(prim, lastVTypeID_, &vshader, &fshader, useHWTransform);
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, pipelineKey, dec_, vshader, fshader, false);
if (!pipeline) {
// Already logged, let's bail out.
return;
}
vkCmdBindPipeline(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); // TODO: Avoid if same as last draw.
if ((dirtyUniforms_ & DIRTY_BASE_UNIFORMS) || baseBuf == VK_NULL_HANDLE) {
baseUBOOffset = shaderManager_->PushBaseBuffer(frame->pushUBO, &baseBuf);
dirtyUniforms_ &= ~DIRTY_BASE_UNIFORMS;
}
// Even if the first draw is through-mode, make sure we at least have one copy of these uniforms buffered
if ((dirtyUniforms_ & DIRTY_LIGHT_UNIFORMS) || lightBuf == VK_NULL_HANDLE) {
lightUBOOffset = shaderManager_->PushLightBuffer(frame->pushUBO, &lightBuf);
dirtyUniforms_ &= ~DIRTY_LIGHT_UNIFORMS;
}
if ((dirtyUniforms_ & DIRTY_BONE_UNIFORMS) || boneBuf == VK_NULL_HANDLE) {
boneUBOOffset = shaderManager_->PushBoneBuffer(frame->pushUBO, &boneBuf);
dirtyUniforms_ &= ~DIRTY_BONE_UNIFORMS;
}
VkDescriptorSet ds = GetDescriptorSet(imageView, sampler, baseBuf, lightBuf, boneBuf);
const uint32_t dynamicUBOOffsets[3] = {
baseUBOOffset, lightUBOOffset, boneUBOOffset,
};
vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets);
if (drawIndexed) {
VkBuffer vbuf, ibuf;
vbOffset = (uint32_t)frame->pushVertex->Push(drawBuffer, maxIndex * sizeof(TransformedVertex), &vbuf);
ibOffset = (uint32_t)frame->pushIndex->Push(inds, sizeof(short) * numTrans, &ibuf);
VkDeviceSize offsets[1] = { vbOffset };
// TODO: Avoid rebinding if the vertex size stays the same by using the offset arguments
vkCmdBindVertexBuffers(cmd_, 0, 1, &vbuf, offsets);
vkCmdBindIndexBuffer(cmd_, ibuf, ibOffset, VK_INDEX_TYPE_UINT16);
vkCmdDrawIndexed(cmd_, numTrans, 1, 0, 0, 0);
} else {
VkBuffer vbuf;
vbOffset = (uint32_t)frame->pushVertex->Push(drawBuffer, numTrans * sizeof(TransformedVertex), &vbuf);
VkDeviceSize offsets[1] = { vbOffset };
// TODO: Avoid rebinding if the vertex size stays the same by using the offset arguments
vkCmdBindVertexBuffers(cmd_, 0, 1, &vbuf, offsets);
vkCmdDraw(cmd_, numTrans, 1, 0, 0);
}
} else if (result.action == SW_CLEAR) {
// Note: we won't get here if the clear is alpha but not color, or color but not alpha.
// A rectangle will be used instead.
// TODO: If this is the first clear in a frame, translate to a cleared attachment load instead.
int mask = gstate.isClearModeColorMask() ? 1 : 0;
if (gstate.isClearModeAlphaMask()) mask |= 2;
if (gstate.isClearModeDepthMask()) mask |= 4;
VkClearValue colorValue, depthValue;
colorValue.color.float32[0] = (result.color & 0xFF) * (1.0f / 255.0f);
colorValue.color.float32[1] = ((result.color >> 8) & 0xFF) * (1.0f / 255.0f);
colorValue.color.float32[2] = ((result.color >> 16) & 0xFF) * (1.0f / 255.0f);
colorValue.color.float32[3] = ((result.color >> 24) & 0xFF) * (1.0f / 255.0f);
depthValue.depthStencil.depth = result.depth;
depthValue.depthStencil.stencil = (result.color >> 24) & 0xFF;
VkClearRect rect;
rect.baseArrayLayer = 0;
rect.layerCount = 1;
rect.rect.offset.x = 0;
rect.rect.offset.y = 0;
rect.rect.extent.width = gstate_c.curRTRenderWidth;
rect.rect.extent.height = gstate_c.curRTRenderHeight;
int count = 0;
VkClearAttachment attach[2];
if (mask & 3) {
attach[count].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
attach[count].clearValue = colorValue;
attach[count].colorAttachment = 0;
count++;
}
if (mask & 4) {
attach[count].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
attach[count].clearValue = depthValue;
attach[count].colorAttachment = 0;
}
vkCmdClearAttachments(cmd_, count, attach, 1, &rect);
if (mask & 1) {
framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason);
}
if (mask & 4) {
framebufferManager_->SetDepthUpdated();
}
}
}
gpuStats.numDrawCalls += numDrawCalls;
gpuStats.numVertsSubmitted += vertexCountInDrawCalls;
indexGen.Reset();
numDrawCalls = 0;
vertexCountInDrawCalls = 0;
prevPrim_ = GE_PRIM_INVALID;
gstate_c.vertexFullAlpha = true;
framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason);
// Now seems as good a time as any to reset the min/max coords, which we may examine later.
gstate_c.vertBounds.minU = 512;
gstate_c.vertBounds.minV = 512;
gstate_c.vertBounds.maxU = 0;
gstate_c.vertBounds.maxV = 0;
host->GPUNotifyDraw();
}
void DrawEngineVulkan::Resized() {
decJitCache_->Clear();
lastVTypeID_ = -1;
dec_ = NULL;
// TODO: We must also wipe pipelines.
for (auto iter = decoderMap_.begin(); iter != decoderMap_.end(); iter++) {
delete iter->second;
}
decoderMap_.clear();
}
bool DrawEngineVulkan::IsCodePtrVertexDecoder(const u8 *ptr) const {
return decJitCache_->IsInSpace(ptr);
}

View File

@ -0,0 +1,240 @@
// Copyright (c) 2015- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
// The Descriptor Set used for the majority of PSP rendering looks like this:
//
// * binding 0: Texture/Sampler (the PSP texture)
// * binding 1: Secondary texture sampler for shader blending or depal palettes
// * binding 2: Base Uniform Buffer (includes fragment state)
// * binding 3: Light uniform buffer
// * binding 4: Bone uniform buffer
//
// All shaders conform to this layout, so they are all compatible with the same descriptor set.
// The format of the various uniform buffers may vary though - vertex shaders that don't skin
// won't get any bone data, etc.
#include <map>
#include <unordered_map>
#include "GPU/Vulkan/VulkanUtil.h"
#include "GPU/GPUState.h"
#include "GPU/Common/GPUDebugInterface.h"
#include "GPU/Common/IndexGenerator.h"
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/DrawEngineCommon.h"
#include "GPU/Common/GPUStateUtils.h"
struct DecVtxFormat;
struct UVScale;
class ShaderManagerVulkan;
class PipelineManagerVulkan;
class TextureCacheVulkan;
class FramebufferManagerVulkan;
class CachedTextureVulkan;
// Avoiding the full include of TextureDecoder.h.
#if (defined(_M_SSE) && defined(_M_X64)) || defined(ARM64)
typedef u64 ReliableHashType;
#else
typedef u32 ReliableHashType;
#endif
class VulkanContext;
class VulkanPushBuffer;
// Handles transform, lighting and drawing.
class DrawEngineVulkan : public DrawEngineCommon {
public:
DrawEngineVulkan(VulkanContext *vulkan);
virtual ~DrawEngineVulkan();
void SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead);
void SetShaderManager(ShaderManagerVulkan *shaderManager) {
shaderManager_ = shaderManager;
}
void SetPipelineManager(PipelineManagerVulkan *pipelineManager) {
pipelineManager_ = pipelineManager;
}
void SetTextureCache(TextureCacheVulkan *textureCache) {
textureCache_ = textureCache;
}
void SetFramebufferManager(FramebufferManagerVulkan *fbManager) {
framebufferManager_ = fbManager;
}
void Resized(); // TODO: Call
void SetupVertexDecoder(u32 vertType);
void SetupVertexDecoderInternal(u32 vertType);
// This requires a SetupVertexDecoder call first.
int EstimatePerVertexCost() {
// TODO: This is transform cost, also account for rasterization cost somehow... although it probably
// runs in parallel with transform.
// Also, this is all pure guesswork. If we can find a way to do measurements, that would be great.
// GTA wants a low value to run smooth, GoW wants a high value (otherwise it thinks things
// went too fast and starts doing all the work over again).
int cost = 20;
if (gstate.isLightingEnabled()) {
cost += 10;
for (int i = 0; i < 4; i++) {
if (gstate.isLightChanEnabled(i))
cost += 10;
}
}
if (gstate.getUVGenMode() != GE_TEXMAP_TEXTURE_COORDS) {
cost += 20;
}
if (dec_ && dec_->morphcount > 1) {
cost += 5 * dec_->morphcount;
}
return cost;
}
// So that this can be inlined
void Flush(VkCommandBuffer cmd) {
if (!numDrawCalls)
return;
DoFlush(cmd);
}
bool IsCodePtrVertexDecoder(const u8 *ptr) const;
void DispatchFlush() override { Flush(cmd_); }
void DispatchSubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) override {
SubmitPrim(verts, inds, prim, vertexCount, vertType, bytesRead);
}
void SetCmdBuffer(VkCommandBuffer cmd) {
cmd_ = cmd;
}
VkPipelineLayout GetPipelineLayout() const {
return pipelineLayout_;
}
void BeginFrame();
void EndFrame();
void DirtyAllUBOs();
private:
void DecodeVerts(VulkanPushBuffer *push, uint32_t *bindOffset, VkBuffer *vkbuf);
void DoFlush(VkCommandBuffer cmd);
VkDescriptorSet GetDescriptorSet(VkImageView imageView, VkSampler sampler, VkBuffer base, VkBuffer light, VkBuffer bone);
VertexDecoder *GetVertexDecoder(u32 vtype);
VulkanContext *vulkan_;
// We use a single descriptor set layout for all PSP draws.
VkDescriptorSetLayout descriptorSetLayout_;
VkPipelineLayout pipelineLayout_;
struct DescriptorSetKey {
VkImageView imageView_;
VkImageView secondaryImageView_;
VkSampler sampler_;
VkBuffer base_, light_, bone_; // All three UBO slots will be set to this. This will usually be identical
// for all draws in a frame, except when the buffer has to grow.
bool operator < (const DescriptorSetKey &other) const {
if (imageView_ < other.imageView_) return true; else if (imageView_ > other.imageView_) return false;
if (sampler_ < other.sampler_) return true; else if (sampler_ > other.sampler_) return false;
if (secondaryImageView_ < other.secondaryImageView_) return true; else if (secondaryImageView_ > other.secondaryImageView_) return false;
if (base_ < other.base_) return true; else if (base_ > other.base_) return false;
if (light_ < other.light_) return true; else if (light_ > other.light_) return false;
if (bone_ < other.bone_) return true; else if (bone_ > other.bone_) return false;
return false;
}
};
// We alternate between these.
struct FrameData {
VkDescriptorPool descPool;
VulkanPushBuffer *pushUBO;
VulkanPushBuffer *pushVertex;
VulkanPushBuffer *pushIndex;
// We do rolling allocation and reset instead of caching across frames. That we might do later.
std::map<DescriptorSetKey, VkDescriptorSet> descSets;
};
int curFrame_;
FrameData frame_[2];
// Defer all vertex decoding to a "Flush" (except when software skinning)
struct DeferredDrawCall {
void *verts;
void *inds;
u32 vertType;
u8 indexType;
s8 prim;
u32 vertexCount;
u16 indexLowerBound;
u16 indexUpperBound;
};
// This is always set to the current main command buffer of the VulkanContext.
// In the future, we may support flushing mid-frame and more fine grained command buffer usage,
// but for now, let's just submit a whole frame at a time. This is not compatible with some games
// that do mid frame read-backs.
VkCommandBuffer cmd_;
// Vertex collector state
IndexGenerator indexGen;
GEPrimitiveType prevPrim_;
u32 lastVTypeID_;
TransformedVertex *transformed;
TransformedVertex *transformedExpanded;
// Other
ShaderManagerVulkan *shaderManager_;
PipelineManagerVulkan *pipelineManager_;
TextureCacheVulkan *textureCache_;
FramebufferManagerVulkan *framebufferManager_;
VkSampler depalSampler_;
enum { MAX_DEFERRED_DRAW_CALLS = 128 };
uint32_t dirtyUniforms_;
uint32_t baseUBOOffset;
uint32_t lightUBOOffset;
uint32_t boneUBOOffset;
VkBuffer baseBuf, lightBuf, boneBuf;
DeferredDrawCall drawCalls[MAX_DEFERRED_DRAW_CALLS];
int numDrawCalls;
int vertexCountInDrawCalls;
bool fboTexNeedBind_;
bool fboTexBound_;
};

View File

@ -0,0 +1,478 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#if !defined(USING_GLES2)
// SDL 1.2 on Apple does not have support for OpenGL 3 and hence needs
// special treatment in the shader generator.
#if defined(__APPLE__)
#define FORCE_OPENGL_2_0
#endif
#endif
#include <cstdio>
#include <sstream>
#include "Common/StringUtils.h"
#include "base/logging.h"
#include "gfx_es2/gpu_features.h"
#include "Core/Reporting.h"
#include "Core/Config.h"
#include "GPU/Common/GPUStateUtils.h"
#include "GPU/Common/ShaderId.h"
#include "GPU/Vulkan/FragmentShaderGeneratorVulkan.h"
#include "GPU/Vulkan/FramebufferVulkan.h"
#include "GPU/Vulkan/ShaderManagerVulkan.h"
#include "GPU/Vulkan/PipelineManagerVulkan.h"
#include "GPU/ge_constants.h"
#include "GPU/GPUState.h"
static const char *vulkan_glsl_preamble =
"#version 400\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"#extension GL_ARB_shading_language_420pack : enable\n\n";
#define WRITE p+=sprintf
// Missing: Z depth range
bool GenerateVulkanGLSLFragmentShader(const ShaderID &id, char *buffer) {
char *p = buffer;
const char *lastFragData = nullptr;
WRITE(p, "%s", vulkan_glsl_preamble);
bool lmode = id.Bit(FS_BIT_LMODE);
bool doTexture = id.Bit(FS_BIT_DO_TEXTURE);
bool enableFog = id.Bit(FS_BIT_ENABLE_FOG);
bool enableAlphaTest = id.Bit(FS_BIT_ALPHA_TEST);
bool alphaTestAgainstZero = id.Bit(FS_BIT_ALPHA_AGAINST_ZERO);
bool enableColorTest = id.Bit(FS_BIT_COLOR_TEST);
bool colorTestAgainstZero = id.Bit(FS_BIT_COLOR_AGAINST_ZERO);
bool enableColorDoubling = id.Bit(FS_BIT_COLOR_DOUBLE);
bool doTextureProjection = id.Bit(FS_BIT_DO_TEXTURE_PROJ);
bool doTextureAlpha = id.Bit(FS_BIT_TEXALPHA);
bool doFlatShading = id.Bit(FS_BIT_FLATSHADE);
GEComparison alphaTestFunc = (GEComparison)id.Bits(FS_BIT_ALPHA_TEST_FUNC, 3);
GEComparison colorTestFunc = (GEComparison)id.Bits(FS_BIT_COLOR_TEST_FUNC, 2);
bool needShaderTexClamp = id.Bit(FS_BIT_SHADER_TEX_CLAMP);
GETexFunc texFunc = (GETexFunc)id.Bits(FS_BIT_TEXFUNC, 3);
bool textureAtOffset = id.Bit(FS_BIT_TEXTURE_AT_OFFSET);
ReplaceBlendType replaceBlend = static_cast<ReplaceBlendType>(id.Bits(FS_BIT_REPLACE_BLEND, 3));
ReplaceAlphaType stencilToAlpha = static_cast<ReplaceAlphaType>(id.Bits(FS_BIT_STENCIL_TO_ALPHA, 2));
GEBlendSrcFactor replaceBlendFuncA = (GEBlendSrcFactor)id.Bits(FS_BIT_BLENDFUNC_A, 4);
GEBlendDstFactor replaceBlendFuncB = (GEBlendDstFactor)id.Bits(FS_BIT_BLENDFUNC_B, 4);
GEBlendMode replaceBlendEq = (GEBlendMode)id.Bits(FS_BIT_BLENDEQ, 3);
StencilValueType replaceAlphaWithStencilType = (StencilValueType)id.Bits(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE, 4);
bool isModeClear = id.Bit(FS_BIT_CLEARMODE);
const char *shading = doFlatShading ? "flat" : "";
WRITE(p, "layout (std140, set = 0, binding = 2) uniform baseUBO {\n%s} base;\n", ub_baseStr);
if (doTexture) {
WRITE(p, "layout (binding = 0) uniform sampler2D tex;\n");
}
if (!isModeClear && replaceBlend > REPLACE_BLEND_STANDARD) {
if (replaceBlend == REPLACE_BLEND_COPY_FBO) {
WRITE(p, "layout (binding = 1) uniform sampler2D fbotex;\n");
}
}
WRITE(p, "layout (location = 1) %s in vec4 v_color0;\n", shading);
if (lmode)
WRITE(p, "layout (location = 2) %s in vec3 v_color1;\n", shading);
if (enableFog) {
WRITE(p, "layout (location = 3) in float v_fogdepth;\n");
}
if (doTexture) {
if (doTextureProjection)
WRITE(p, "layout (location = 0) in vec3 v_texcoord;\n");
else
WRITE(p, "layout (location = 0) in vec2 v_texcoord;\n");
}
if (enableAlphaTest && !alphaTestAgainstZero) {
WRITE(p, "int roundAndScaleTo255i(in float x) { return int(floor(x * 255.0 + 0.5)); }\n");
}
if (enableColorTest && !colorTestAgainstZero) {
WRITE(p, "ivec3 roundAndScaleTo255iv(in vec3 x) { return ivec3(floor(x * 255.0 + 0.5)); }\n");
}
WRITE(p, "layout (location = 0, index = 0) out vec4 fragColor0;\n");
if (stencilToAlpha == REPLACE_ALPHA_DUALSOURCE) {
WRITE(p, "layout (location = 0, index = 1) out vec4 fragColor1;\n");
}
// PowerVR needs a custom modulo function. For some reason, this has far higher precision than the builtin one.
if ((gl_extensions.bugs & BUG_PVR_SHADER_PRECISION_BAD) && needShaderTexClamp) {
WRITE(p, "float mymod(float a, float b) { return a - b * floor(a / b); }\n");
}
WRITE(p, "void main() {\n");
if (isModeClear) {
// Clear mode does not allow any fancy shading.
WRITE(p, " vec4 v = v_color0;\n");
} else {
const char *secondary = "";
// Secondary color for specular on top of texture
if (lmode) {
WRITE(p, " vec4 s = vec4(v_color1, 0.0);\n");
secondary = " + s";
} else {
secondary = "";
}
if (doTexture) {
const char *texcoord = "v_texcoord";
// TODO: Not sure the right way to do this for projection.
// This path destroys resolution on older PowerVR no matter what I do, so we disable it on SGX 540 and lesser, and live with the consequences.
if (needShaderTexClamp && !(gl_extensions.bugs & BUG_PVR_SHADER_PRECISION_TERRIBLE)) {
// We may be clamping inside a larger surface (tex = 64x64, buffer=480x272).
// We may also be wrapping in such a surface, or either one in a too-small surface.
// Obviously, clamping to a smaller surface won't work. But better to clamp to something.
std::string ucoord = "v_texcoord.x";
std::string vcoord = "v_texcoord.y";
if (doTextureProjection) {
ucoord = "(v_texcoord.x / v_texcoord.z)";
vcoord = "(v_texcoord.y / v_texcoord.z)";
}
std::string modulo = (gl_extensions.bugs & BUG_PVR_SHADER_PRECISION_BAD) ? "mymod" : "mod";
if (id.Bit(FS_BIT_CLAMP_S)) {
ucoord = "clamp(" + ucoord + ", base.texclamp.z, base.texclamp.x - base.texclamp.z)";
} else {
ucoord = modulo + "(" + ucoord + ", base.texclamp.x)";
}
if (id.Bit(FS_BIT_CLAMP_T)) {
vcoord = "clamp(" + vcoord + ", base.texclamp.w, base.texclamp.y - base.texclamp.w)";
} else {
vcoord = modulo + "(" + vcoord + ", base.texclamp.y)";
}
if (textureAtOffset) {
ucoord = "(" + ucoord + " + base.texclampoff.x)";
vcoord = "(" + vcoord + " + base.texclampoff.y)";
}
WRITE(p, " vec2 fixedcoord = vec2(%s, %s);\n", ucoord.c_str(), vcoord.c_str());
texcoord = "fixedcoord";
// We already projected it.
doTextureProjection = false;
}
if (doTextureProjection) {
WRITE(p, " vec4 t = textureProj(tex, %s);\n", texcoord);
} else {
WRITE(p, " vec4 t = texture(tex, %s);\n", texcoord);
}
WRITE(p, " vec4 p = v_color0;\n");
if (doTextureAlpha) { // texfmt == RGBA
switch (texFunc) {
case GE_TEXFUNC_MODULATE:
WRITE(p, " vec4 v = p * t%s;\n", secondary);
break;
case GE_TEXFUNC_DECAL:
WRITE(p, " vec4 v = vec4(mix(p.rgb, t.rgb, t.a), p.a)%s;\n", secondary);
break;
case GE_TEXFUNC_BLEND:
WRITE(p, " vec4 v = vec4(mix(p.rgb, base.texenv.rgb, t.rgb), p.a * t.a)%s;\n", secondary);
break;
case GE_TEXFUNC_REPLACE:
WRITE(p, " vec4 v = t%s;\n", secondary);
break;
case GE_TEXFUNC_ADD:
case GE_TEXFUNC_UNKNOWN1:
case GE_TEXFUNC_UNKNOWN2:
case GE_TEXFUNC_UNKNOWN3:
WRITE(p, " vec4 v = vec4(p.rgb + t.rgb, p.a * t.a)%s;\n", secondary);
break;
default:
WRITE(p, " vec4 v = p;\n"); break;
}
} else { // texfmt == RGB
switch (texFunc) {
case GE_TEXFUNC_MODULATE:
WRITE(p, " vec4 v = vec4(t.rgb * p.rgb, p.a)%s;\n", secondary);
break;
case GE_TEXFUNC_DECAL:
WRITE(p, " vec4 v = vec4(t.rgb, p.a)%s;\n", secondary);
break;
case GE_TEXFUNC_BLEND:
WRITE(p, " vec4 v = vec4(mix(p.rgb, base.texenv.rgb, t.rgb), p.a)%s;\n", secondary);
break;
case GE_TEXFUNC_REPLACE:
WRITE(p, " vec4 v = vec4(t.rgb, p.a)%s;\n", secondary);
break;
case GE_TEXFUNC_ADD:
case GE_TEXFUNC_UNKNOWN1:
case GE_TEXFUNC_UNKNOWN2:
case GE_TEXFUNC_UNKNOWN3:
WRITE(p, " vec4 v = vec4(p.rgb + t.rgb, p.a)%s;\n", secondary); break;
default:
WRITE(p, " vec4 v = p;\n"); break;
}
}
} else {
// No texture mapping
WRITE(p, " vec4 v = v_color0 %s;\n", secondary);
}
// Texture access is at half texels [0.5/256, 255.5/256], but colors are normalized [0, 255].
// So we have to scale to account for the difference.
std::string alphaTestXCoord = "0";
if (enableAlphaTest) {
if (alphaTestAgainstZero) {
// When testing against 0 (extremely common), we can avoid some math.
// 0.002 is approximately half of 1.0 / 255.0.
if (alphaTestFunc == GE_COMP_NOTEQUAL || alphaTestFunc == GE_COMP_GREATER) {
WRITE(p, " if (v.a < 0.002) discard;\n");
} else if (alphaTestFunc != GE_COMP_NEVER) {
// Anything else is a test for == 0. Happens sometimes, actually...
WRITE(p, " if (v.a > 0.002) discard;\n");
} else {
// NEVER has been logged as used by games, although it makes little sense - statically failing.
// Maybe we could discard the drawcall, but it's pretty rare. Let's just statically discard here.
WRITE(p, " discard;\n");
}
} else {
const char *alphaTestFuncs[] = { "#", "#", " != ", " == ", " >= ", " > ", " <= ", " < " };
if (alphaTestFuncs[alphaTestFunc][0] != '#') {
WRITE(p, " if ((roundAndScaleTo255i(v.a) & base.alphacolormask.a) %s base.alphacolorref.a) discard;\n", alphaTestFuncs[alphaTestFunc]);
} else {
// This means NEVER. See above.
WRITE(p, " discard;\n");
}
}
}
if (enableColorTest) {
if (colorTestAgainstZero) {
// When testing against 0 (common), we can avoid some math.
// 0.002 is approximately half of 1.0 / 255.0.
if (colorTestFunc == GE_COMP_NOTEQUAL) {
WRITE(p, " if (v.r < 0.002 && v.g < 0.002 && v.b < 0.002) discard;\n");
} else if (colorTestFunc != GE_COMP_NEVER) {
// Anything else is a test for == 0.
WRITE(p, " if (v.r > 0.002 || v.g > 0.002 || v.b > 0.002) discard;\n");
} else {
// NEVER has been logged as used by games, although it makes little sense - statically failing.
// Maybe we could discard the drawcall, but it's pretty rare. Let's just statically discard here.
WRITE(p, " discard;\n");
}
} else {
const char *colorTestFuncs[] = { "#", "#", " != ", " == " };
if (colorTestFuncs[colorTestFunc][0] != '#') {
WRITE(p, " ivec3 v_scaled = roundAndScaleTo255iv(v.rgb);\n");
WRITE(p, " if ((v_scaled & base.alphacolormask.rgb) %s (base.alphacolorref.rgb & base.alphacolormask.rgb)) discard;\n", colorTestFuncs[colorTestFunc]);
} else {
WRITE(p, " discard;\n");
}
}
}
// Color doubling happens after the color test.
if (enableColorDoubling && replaceBlend == REPLACE_BLEND_2X_SRC) {
WRITE(p, " v.rgb = v.rgb * 4.0;\n");
} else if (enableColorDoubling || replaceBlend == REPLACE_BLEND_2X_SRC) {
WRITE(p, " v.rgb = v.rgb * 2.0;\n");
}
if (enableFog) {
WRITE(p, " float fogCoef = clamp(v_fogdepth, 0.0, 1.0);\n");
WRITE(p, " v = mix(vec4(base.fogcolor, v.a), v, fogCoef);\n");
// WRITE(p, " v.x = v_depth;\n");
}
if (replaceBlend == REPLACE_BLEND_PRE_SRC || replaceBlend == REPLACE_BLEND_PRE_SRC_2X_ALPHA) {
const char *srcFactor = "ERROR";
switch (replaceBlendFuncA) {
case GE_SRCBLEND_DSTCOLOR: srcFactor = "ERROR"; break;
case GE_SRCBLEND_INVDSTCOLOR: srcFactor = "ERROR"; break;
case GE_SRCBLEND_SRCALPHA: srcFactor = "vec3(v.a)"; break;
case GE_SRCBLEND_INVSRCALPHA: srcFactor = "vec3(1.0 - v.a)"; break;
case GE_SRCBLEND_DSTALPHA: srcFactor = "ERROR"; break;
case GE_SRCBLEND_INVDSTALPHA: srcFactor = "ERROR"; break;
case GE_SRCBLEND_DOUBLESRCALPHA: srcFactor = "vec3(v.a * 2.0)"; break;
case GE_SRCBLEND_DOUBLEINVSRCALPHA: srcFactor = "vec3(1.0 - v.a * 2.0)"; break;
case GE_SRCBLEND_DOUBLEDSTALPHA: srcFactor = "ERROR"; break;
case GE_SRCBLEND_DOUBLEINVDSTALPHA: srcFactor = "ERROR"; break;
case GE_SRCBLEND_FIXA: srcFactor = "base.blendFixA"; break;
}
WRITE(p, " v.rgb = v.rgb * %s;\n", srcFactor);
}
if (replaceBlend == REPLACE_BLEND_COPY_FBO) {
WRITE(p, " lowp vec4 destColor = texelFetch(fbotex, ivec2(gl_FragCoord.x, gl_FragCoord.y), 0);\n");
const char *srcFactor = "vec3(1.0)";
const char *dstFactor = "vec3(0.0)";
switch (replaceBlendFuncA) {
case GE_SRCBLEND_DSTCOLOR: srcFactor = "destColor.rgb"; break;
case GE_SRCBLEND_INVDSTCOLOR: srcFactor = "(vec3(1.0) - destColor.rgb)"; break;
case GE_SRCBLEND_SRCALPHA: srcFactor = "vec3(v.a)"; break;
case GE_SRCBLEND_INVSRCALPHA: srcFactor = "vec3(1.0 - v.a)"; break;
case GE_SRCBLEND_DSTALPHA: srcFactor = "vec3(destColor.a)"; break;
case GE_SRCBLEND_INVDSTALPHA: srcFactor = "vec3(1.0 - destColor.a)"; break;
case GE_SRCBLEND_DOUBLESRCALPHA: srcFactor = "vec3(v.a * 2.0)"; break;
case GE_SRCBLEND_DOUBLEINVSRCALPHA: srcFactor = "vec3(1.0 - v.a * 2.0)"; break;
case GE_SRCBLEND_DOUBLEDSTALPHA: srcFactor = "vec3(destColor.a * 2.0)"; break;
case GE_SRCBLEND_DOUBLEINVDSTALPHA: srcFactor = "vec3(1.0 - destColor.a * 2.0)"; break;
case GE_SRCBLEND_FIXA: srcFactor = "base.blendFixA"; break;
}
switch (replaceBlendFuncB) {
case GE_DSTBLEND_SRCCOLOR: dstFactor = "v.rgb"; break;
case GE_DSTBLEND_INVSRCCOLOR: dstFactor = "(vec3(1.0) - v.rgb)"; break;
case GE_DSTBLEND_SRCALPHA: dstFactor = "vec3(v.a)"; break;
case GE_DSTBLEND_INVSRCALPHA: dstFactor = "vec3(1.0 - v.a)"; break;
case GE_DSTBLEND_DSTALPHA: dstFactor = "vec3(destColor.a)"; break;
case GE_DSTBLEND_INVDSTALPHA: dstFactor = "vec3(1.0 - destColor.a)"; break;
case GE_DSTBLEND_DOUBLESRCALPHA: dstFactor = "vec3(v.a * 2.0)"; break;
case GE_DSTBLEND_DOUBLEINVSRCALPHA: dstFactor = "vec3(1.0 - v.a * 2.0)"; break;
case GE_DSTBLEND_DOUBLEDSTALPHA: dstFactor = "vec3(destColor.a * 2.0)"; break;
case GE_DSTBLEND_DOUBLEINVDSTALPHA: dstFactor = "vec3(1.0 - destColor.a * 2.0)"; break;
case GE_DSTBLEND_FIXB: dstFactor = "base.blendFixB"; break;
}
switch (replaceBlendEq) {
case GE_BLENDMODE_MUL_AND_ADD:
WRITE(p, " v.rgb = v.rgb * %s + destColor.rgb * %s;\n", srcFactor, dstFactor);
break;
case GE_BLENDMODE_MUL_AND_SUBTRACT:
WRITE(p, " v.rgb = v.rgb * %s - destColor.rgb * %s;\n", srcFactor, dstFactor);
break;
case GE_BLENDMODE_MUL_AND_SUBTRACT_REVERSE:
WRITE(p, " v.rgb = destColor.rgb * %s - v.rgb * %s;\n", dstFactor, srcFactor);
break;
case GE_BLENDMODE_MIN:
WRITE(p, " v.rgb = min(v.rgb, destColor.rgb);\n");
break;
case GE_BLENDMODE_MAX:
WRITE(p, " v.rgb = max(v.rgb, destColor.rgb);\n");
break;
case GE_BLENDMODE_ABSDIFF:
WRITE(p, " v.rgb = abs(v.rgb - destColor.rgb);\n");
break;
}
}
if (replaceBlend == REPLACE_BLEND_2X_ALPHA || replaceBlend == REPLACE_BLEND_PRE_SRC_2X_ALPHA) {
WRITE(p, " v.a = v.a * 2.0;\n");
}
}
std::string replacedAlpha = "0.0";
char replacedAlphaTemp[64] = "";
if (stencilToAlpha != REPLACE_ALPHA_NO) {
switch (replaceAlphaWithStencilType) {
case STENCIL_VALUE_UNIFORM:
replacedAlpha = "base.fogcoef_stencilreplace.z";
break;
case STENCIL_VALUE_ZERO:
replacedAlpha = "0.0";
break;
case STENCIL_VALUE_ONE:
case STENCIL_VALUE_INVERT:
// In invert, we subtract by one, but we want to output one here.
replacedAlpha = "1.0";
break;
case STENCIL_VALUE_INCR_4:
case STENCIL_VALUE_DECR_4:
// We're adding/subtracting, just by the smallest value in 4-bit.
snprintf(replacedAlphaTemp, sizeof(replacedAlphaTemp), "%f", 1.0 / 15.0);
replacedAlpha = replacedAlphaTemp;
break;
case STENCIL_VALUE_INCR_8:
case STENCIL_VALUE_DECR_8:
// We're adding/subtracting, just by the smallest value in 8-bit.
snprintf(replacedAlphaTemp, sizeof(replacedAlphaTemp), "%f", 1.0 / 255.0);
replacedAlpha = replacedAlphaTemp;
break;
case STENCIL_VALUE_KEEP:
// Do nothing. We'll mask out the alpha using color mask.
break;
}
}
switch (stencilToAlpha) {
case REPLACE_ALPHA_DUALSOURCE:
WRITE(p, " fragColor0 = vec4(v.rgb, %s);\n", replacedAlpha.c_str());
WRITE(p, " fragColor1 = vec4(0.0, 0.0, 0.0, v.a);\n");
break;
case REPLACE_ALPHA_YES:
WRITE(p, " fragColor0 = vec4(v.rgb, %s);\n", replacedAlpha.c_str());
break;
case REPLACE_ALPHA_NO:
WRITE(p, " fragColor0 = v;\n");
break;
default:
ERROR_LOG(G3D, "Bad stencil-to-alpha type, corrupt ID?");
return false;
}
LogicOpReplaceType replaceLogicOpType = (LogicOpReplaceType)id.Bits(FS_BIT_REPLACE_LOGIC_OP_TYPE, 2);
switch (replaceLogicOpType) {
case LOGICOPTYPE_ONE:
WRITE(p, " fragColor0.rgb = vec3(1.0, 1.0, 1.0);\n");
break;
case LOGICOPTYPE_INVERT:
WRITE(p, " fragColor0.rgb = vec3(1.0, 1.0, 1.0) - fragColor0.rgb;\n");
break;
case LOGICOPTYPE_NORMAL:
break;
default:
ERROR_LOG(G3D, "Bad logic op type, corrupt ID?");
return false;
}
if (gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT)) {
WRITE(p, " highp float z = gl_FragCoord.z;\n");
WRITE(p, " z = (1.0/65535.0) * floor(z * 65535.0);\n");
WRITE(p, " gl_FragDepth = z;\n");
}
WRITE(p, "}\n");
return true;
}

View File

@ -1,3 +1,4 @@
#pragma once
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
@ -17,23 +18,8 @@
#pragma once
#include "StubHost.h"
#include "WindowsHeadlessHost.h"
#include "Globals.h"
#undef HEADLESSHOST_CLASS
#define HEADLESSHOST_CLASS WindowsHeadlessHost
struct ShaderID;
#include "Common/CommonWindows.h"
// TODO: Get rid of this junk
class WindowsHeadlessHostDx9 : public WindowsHeadlessHost
{
public:
bool InitGraphics(std::string *error_message, GraphicsContext **graphicsContext) override;
void ShutdownGraphics() override;
void SwapBuffers() override;
private:
bool ResizeGL() override;
};
bool GenerateVulkanGLSLFragmentShader(const ShaderID &id, char *buffer);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,197 @@
// Copyright (c) 2015- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include "Common/Vulkan/VulkanLoader.h"
#include "GPU/Common/FramebufferCommon.h"
#include "GPU/GPUInterface.h"
#include "GPU/Common/GPUDebugInterface.h"
#include "GPU/Vulkan/VulkanUtil.h"
// TODO: WTF?
enum VulkanFBOColorDepth {
VK_FBO_8888,
VK_FBO_565,
VK_FBO_4444,
VK_FBO_5551,
};
class TextureCacheVulkan;
class DrawEngineVulkan;
class VulkanContext;
class ShaderManagerVulkan;
class VulkanTexture;
struct PostShaderUniforms {
float texelDelta[2]; float pad[2];
float pixelDelta[2]; float pad0[2];
float time[4];
};
// Simple struct for asynchronous PBO readbacks
// TODO: Probably will need a complete redesign.
struct AsyncPBOVulkan {
// handle;
u32 maxSize;
u32 fb_address;
u32 stride;
u32 height;
u32 size;
GEBufferFormat format;
bool reading;
};
struct CardboardSettings {
bool enabled;
float leftEyeXPosition;
float rightEyeXPosition;
float screenYPosition;
float screenWidth;
float screenHeight;
};
class FramebufferManagerVulkan : public FramebufferManagerCommon {
public:
FramebufferManagerVulkan(VulkanContext *vulkan);
~FramebufferManagerVulkan();
void SetTextureCache(TextureCacheVulkan *tc) {
textureCache_ = tc;
}
void SetShaderManager(ShaderManagerVulkan *sm) {
shaderManager_ = sm;
}
void SetDrawEngine(DrawEngineVulkan *td) {
transformDraw_ = td;
}
void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height) override;
void DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height) override;
void DrawFramebufferToOutput(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, bool applyPostShader) override;
// If texture != 0, will bind it.
// x,y,w,h are relative to destW, destH which fill out the target completely.
void DrawActiveTexture(VulkanTexture *texture, float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, VkPipeline pipeline, int uvRotation);
void DestroyAllFBOs();
virtual void Init() override;
void EndFrame();
void Resized();
void DeviceLost();
void CopyDisplayToOutput();
int GetLineWidth();
void ReformatFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat old);
void BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst);
// For use when texturing from a framebuffer. May create a duplicate if target.
void BindFramebufferColor(int stage, u32 fbRawAddress, VirtualFramebuffer *framebuffer, int flags);
// Reads a rectangular subregion of a framebuffer to the right position in its backing memory.
void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) override;
void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) override;
std::vector<FramebufferInfo> GetFramebufferList();
bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override;
void DestroyFramebuf(VirtualFramebuffer *vfb) override;
void ResizeFramebufFBO(VirtualFramebuffer *vfb, u16 w, u16 h, bool force = false) override;
bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer);
bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer);
bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer);
static bool GetDisplayFramebuffer(GPUDebugBuffer &buffer);
virtual void RebindFramebuffer() override;
VulkanFBO *GetTempFBO(u16 w, u16 h, VulkanFBOColorDepth depth = VK_FBO_8888);
// Cardboard Settings Calculator
struct CardboardSettings * GetCardboardSettings(struct CardboardSettings * cardboardSettings);
protected:
virtual void DisableState() override;
virtual void ClearBuffer(bool keepState = false);
virtual void FlushBeforeCopy() override;
virtual void DecimateFBOs() override;
// Used by ReadFramebufferToMemory and later framebuffer block copies
virtual void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) override;
virtual void NotifyRenderFramebufferCreated(VirtualFramebuffer *vfb) override;
virtual void NotifyRenderFramebufferSwitched(VirtualFramebuffer *prevVfb, VirtualFramebuffer *vfb, bool isClearingDepth) override;
virtual void NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb, bool vfbFormatChanged) override;
virtual bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) override;
virtual void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override;
private:
void UpdatePostShaderUniforms(int bufferWidth, int bufferHeight, int renderWidth, int renderHeight);
void CompileDraw2DProgram();
void DestroyDraw2DProgram();
void SetNumExtraFBOs(int num);
void PackFramebufferAsync_(VirtualFramebuffer *vfb); // Not used under ES currently
void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h);
VulkanContext *vulkan_;
// The command buffer of the current framebuffer pass being rendered to.
// One framebuffer can be used as a texturing source at multiple times in a frame,
// but then the contents have to be copied out into a new texture every time.
VkCommandBuffer curCmd_;
DrawEngineVulkan *drawEngine_;
// Used by DrawPixels
VulkanTexture *drawPixelsTex_;
GEBufferFormat drawPixelsTexFormat_;
int drawPixelsTexW_;
int drawPixelsTexH_;
u8 *convBuf_;
u32 convBufSize_;
TextureCacheVulkan *textureCache_;
ShaderManagerVulkan *shaderManager_;
DrawEngineVulkan *transformDraw_;
// Used by post-processing shader
std::vector<FBO *> extraFBOs_;
bool resized_;
struct TempFBO {
VulkanFBO *fbo_vk;
int last_frame_used;
};
std::map<u64, TempFBO> tempFBOs_;
// Not used under ES currently.
AsyncPBOVulkan *pixelBufObj_; //this isn't that large
u8 currentPBO_;
// This gets copied to the current frame's push buffer as needed.
PostShaderUniforms postUniforms_;
};

2171
GPU/Vulkan/GPU_Vulkan.cpp Normal file

File diff suppressed because it is too large Load Diff

198
GPU/Vulkan/GPU_Vulkan.h Normal file
View File

@ -0,0 +1,198 @@
// Copyright (c) 2015- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include <list>
#include <deque>
#include "GPU/GPUCommon.h"
#include "GPU/Vulkan/DrawEngineVulkan.h"
#include "GPU/Vulkan/PipelineManagerVulkan.h"
#include "GPU/Vulkan/TextureCacheVulkan.h"
#include "GPU/Vulkan/DepalettizeShaderVulkan.h"
class FramebufferManagerVulkan;
class ShaderManagerVulkan;
class LinkedShader;
class GPU_Vulkan : public GPUCommon {
public:
GPU_Vulkan(GraphicsContext *ctx);
~GPU_Vulkan();
// This gets called on startup and when we get back from settings.
void CheckGPUFeatures();
// These are where we can reset command buffers etc.
void BeginHostFrame();
void EndHostFrame();
void InitClear() override;
void Reinitialize() override;
void PreExecuteOp(u32 op, u32 diff) override;
void Execute_Generic(u32 op, u32 diff);
void ExecuteOp(u32 op, u32 diff) override;
void ReapplyGfxStateInternal() override;
void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) override;
void CopyDisplayToOutput() override;
void NotifyVideoUpload(u32 addr, int size, int width, int format) override;
void BeginFrame() override;
void UpdateStats() override;
void InvalidateCache(u32 addr, int size, GPUInvalidationType type) override;
bool PerformMemoryCopy(u32 dest, u32 src, int size) override;
bool PerformMemorySet(u32 dest, u8 v, int size) override;
bool PerformMemoryDownload(u32 dest, int size) override;
bool PerformMemoryUpload(u32 dest, int size) override;
bool PerformStencilUpload(u32 dest, int size) override;
void ClearCacheNextFrame() override;
void DeviceLost() override; // Only happens on Android. Drop all textures and shaders.
void DumpNextFrame() override;
void DoState(PointerWrap &p) override;
// Called by the window system if the window size changed. This will be reflected in PSPCoreParam.pixel*.
void Resized() override;
void ClearShaderCache() override;
bool DecodeTexture(u8 *dest, const GPUgstate &state) override {
return false;
}
bool FramebufferDirty() override;
bool FramebufferReallyDirty() override;
void GetReportingInfo(std::string &primaryInfo, std::string &fullInfo) override {
primaryInfo = reportingPrimaryInfo_;
fullInfo = reportingFullInfo_;
}
typedef void (GPU_Vulkan::*CmdFunc)(u32 op, u32 diff);
struct CommandInfo {
u8 flags;
GPU_Vulkan::CmdFunc func;
};
void Execute_Vaddr(u32 op, u32 diff);
void Execute_Iaddr(u32 op, u32 diff);
void Execute_Prim(u32 op, u32 diff);
void Execute_Bezier(u32 op, u32 diff);
void Execute_Spline(u32 op, u32 diff);
void Execute_BoundingBox(u32 op, u32 diff);
void Execute_VertexType(u32 op, u32 diff);
void Execute_VertexTypeSkinning(u32 op, u32 diff);
void Execute_Region(u32 op, u32 diff);
void Execute_Scissor(u32 op, u32 diff);
void Execute_FramebufType(u32 op, u32 diff);
void Execute_ViewportType(u32 op, u32 diff);
void Execute_ViewportZType(u32 op, u32 diff);
void Execute_TexScaleU(u32 op, u32 diff);
void Execute_TexScaleV(u32 op, u32 diff);
void Execute_TexOffsetU(u32 op, u32 diff);
void Execute_TexOffsetV(u32 op, u32 diff);
void Execute_TexAddr0(u32 op, u32 diff);
void Execute_TexAddrN(u32 op, u32 diff);
void Execute_TexBufw0(u32 op, u32 diff);
void Execute_TexBufwN(u32 op, u32 diff);
void Execute_TexSize0(u32 op, u32 diff);
void Execute_TexSizeN(u32 op, u32 diff);
void Execute_TexFormat(u32 op, u32 diff);
void Execute_TexMapMode(u32 op, u32 diff);
void Execute_TexParamType(u32 op, u32 diff);
void Execute_TexEnvColor(u32 op, u32 diff);
void Execute_TexLevel(u32 op, u32 diff);
void Execute_LoadClut(u32 op, u32 diff);
void Execute_ClutFormat(u32 op, u32 diff);
void Execute_Ambient(u32 op, u32 diff);
void Execute_MaterialDiffuse(u32 op, u32 diff);
void Execute_MaterialEmissive(u32 op, u32 diff);
void Execute_MaterialAmbient(u32 op, u32 diff);
void Execute_MaterialSpecular(u32 op, u32 diff);
void Execute_Light0Param(u32 op, u32 diff);
void Execute_Light1Param(u32 op, u32 diff);
void Execute_Light2Param(u32 op, u32 diff);
void Execute_Light3Param(u32 op, u32 diff);
void Execute_FogColor(u32 op, u32 diff);
void Execute_FogCoef(u32 op, u32 diff);
void Execute_ColorTestMask(u32 op, u32 diff);
void Execute_AlphaTest(u32 op, u32 diff);
void Execute_StencilTest(u32 op, u32 diff);
void Execute_ColorRef(u32 op, u32 diff);
void Execute_WorldMtxNum(u32 op, u32 diff);
void Execute_WorldMtxData(u32 op, u32 diff);
void Execute_ViewMtxNum(u32 op, u32 diff);
void Execute_ViewMtxData(u32 op, u32 diff);
void Execute_ProjMtxNum(u32 op, u32 diff);
void Execute_ProjMtxData(u32 op, u32 diff);
void Execute_TgenMtxNum(u32 op, u32 diff);
void Execute_TgenMtxData(u32 op, u32 diff);
void Execute_BoneMtxNum(u32 op, u32 diff);
void Execute_BoneMtxData(u32 op, u32 diff);
void Execute_BlockTransferStart(u32 op, u32 diff);
// Using string because it's generic - makes no assumptions on the size of the shader IDs of this backend.
std::vector<std::string> DebugGetShaderIDs(DebugShaderType shader) override;
std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) override;
std::vector<FramebufferInfo> GetFramebufferList();
bool GetCurrentSimpleVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices);
bool DescribeCodePtr(const u8 *ptr, std::string &name);
protected:
void FastRunLoop(DisplayList &list) override;
void ProcessEvent(GPUEvent ev) override;
void FastLoadBoneMatrix(u32 target) override;
void FinishDeferred() override;
private:
void Flush() {
drawEngine_.Flush(nullptr);
}
void DoBlockTransfer(u32 skipDrawReason);
void CheckFlushOp(int cmd, u32 diff);
void BuildReportingInfo();
void InitClearInternal();
void BeginFrameInternal();
void CopyDisplayToOutputInternal();
void PerformMemoryCopyInternal(u32 dest, u32 src, int size);
void PerformMemorySetInternal(u32 dest, u8 v, int size);
void PerformStencilUploadInternal(u32 dest, int size);
void InvalidateCacheInternal(u32 addr, int size, GPUInvalidationType type);
void ReinitializeInternal();
inline void UpdateVsyncInterval(bool force);
void UpdateCmdInfo();
static CommandInfo cmdInfo_[256];
GraphicsContext *gfxCtx_;
VulkanContext *vulkan_;
FramebufferManagerVulkan *framebufferManager_;
TextureCacheVulkan textureCache_;
DepalShaderCacheVulkan depalShaderCache_;
DrawEngineVulkan drawEngine_;
// Manages shaders and UBO data
ShaderManagerVulkan *shaderManager_;
// Manages state and pipeline objects
PipelineManagerVulkan *pipelineManager_;
bool resized_;
int lastVsync_;
VkCommandBuffer curCmd_;
std::string reportingPrimaryInfo_;
std::string reportingFullInfo_;
};

View File

@ -0,0 +1,339 @@
#include <cstring>
#include "Common/Log.h"
#include "Common/StringUtils.h"
#include "Common/Vulkan/VulkanContext.h"
#include "GPU/Vulkan/VulkanUtil.h"
#include "GPU/Vulkan/PipelineManagerVulkan.h"
#include "GPU/Vulkan/ShaderManagerVulkan.h"
PipelineManagerVulkan::PipelineManagerVulkan(VulkanContext *vulkan) : vulkan_(vulkan) {
pipelineCache_ = vulkan->CreatePipelineCache();
}
PipelineManagerVulkan::~PipelineManagerVulkan() {
Clear();
vulkan_->Delete().QueueDeletePipelineCache(pipelineCache_);
}
void PipelineManagerVulkan::Clear() {
// This should kill off all the shaders at once.
// This could also be an opportunity to store the whole cache to disk. Will need to also
// store the keys.
for (auto iter : pipelines_) {
delete iter.second;
}
pipelines_.clear();
}
struct DeclTypeInfo {
VkFormat type;
const char *name;
};
static const DeclTypeInfo VComp[] = {
{ VK_FORMAT_UNDEFINED, "NULL" }, // DEC_NONE,
{ VK_FORMAT_R32_SFLOAT, "R32_SFLOAT " }, // DEC_FLOAT_1,
{ VK_FORMAT_R32G32_SFLOAT, "R32G32_SFLOAT " }, // DEC_FLOAT_2,
{ VK_FORMAT_R32G32B32_SFLOAT, "R32G32B32_SFLOAT " }, // DEC_FLOAT_3,
{ VK_FORMAT_R32G32B32A32_SFLOAT, "R32G32B32A32_SFLOAT " }, // DEC_FLOAT_4,
{ VK_FORMAT_R8G8B8A8_SNORM, "R8G8B8A8_SNORM" }, // DEC_S8_3,
{ VK_FORMAT_R16G16B16A16_SNORM, "R16G16B16A16_SNORM " }, // DEC_S16_3,
{ VK_FORMAT_R8G8B8A8_UNORM, "R8G8B8A8_UNORM " }, // DEC_U8_1,
{ VK_FORMAT_R8G8B8A8_UNORM, "R8G8B8A8_UNORM " }, // DEC_U8_2,
{ VK_FORMAT_R8G8B8A8_UNORM, "R8G8B8A8_UNORM " }, // DEC_U8_3,
{ VK_FORMAT_R8G8B8A8_UNORM, "R8G8B8A8_UNORM " }, // DEC_U8_4,
{ VK_FORMAT_R16G16_UNORM, "R16G16_UNORM" }, // DEC_U16_1,
{ VK_FORMAT_R16G16_UNORM, "R16G16_UNORM" }, // DEC_U16_2,
{ VK_FORMAT_R16G16B16A16_UNORM, "R16G16B16A16_UNORM " }, // DEC_U16_3,
{ VK_FORMAT_R16G16B16A16_UNORM, "R16G16B16A16_UNORM " }, // DEC_U16_4,
{ VK_FORMAT_R8G8_UINT, "R8G8_UINT" }, // DEC_U8A_2,
{ VK_FORMAT_R16G16_UINT, "R16G16_UINT" }, // DEC_U16A_2,
};
void VertexAttribSetup(VkVertexInputAttributeDescription *attr, int fmt, int offset, PspAttributeLocation location) {
attr->location = (uint32_t)location;
attr->binding = 0;
attr->format = VComp[fmt].type;
attr->offset = offset;
}
// Returns the number of attributes that were set.
// We could cache these AttributeDescription arrays (with pspFmt as the key), but hardly worth bothering
// as we will only call this code when we need to create a new VkPipeline.
int SetupVertexAttribs(VkVertexInputAttributeDescription attrs[], const DecVtxFormat &decFmt) {
int count = 0;
if (decFmt.w0fmt != 0) {
VertexAttribSetup(&attrs[count++], decFmt.w0fmt, decFmt.w0off, PspAttributeLocation::W1);
}
if (decFmt.w1fmt != 0) {
VertexAttribSetup(&attrs[count++], decFmt.w1fmt, decFmt.w1off, PspAttributeLocation::W2);
}
if (decFmt.uvfmt != 0) {
VertexAttribSetup(&attrs[count++], decFmt.uvfmt, decFmt.uvoff, PspAttributeLocation::TEXCOORD);
}
if (decFmt.c0fmt != 0) {
VertexAttribSetup(&attrs[count++], decFmt.c0fmt, decFmt.c0off, PspAttributeLocation::COLOR0);
}
if (decFmt.c1fmt != 0) {
VertexAttribSetup(&attrs[count++], decFmt.c1fmt, decFmt.c1off, PspAttributeLocation::COLOR1);
}
if (decFmt.nrmfmt != 0) {
VertexAttribSetup(&attrs[count++], decFmt.nrmfmt, decFmt.nrmoff, PspAttributeLocation::NORMAL);
}
// Position is always there.
VertexAttribSetup(&attrs[count++], decFmt.posfmt, decFmt.posoff, PspAttributeLocation::POSITION);
return count;
}
int SetupVertexAttribsPretransformed(VkVertexInputAttributeDescription attrs[], const DecVtxFormat &decFmt) {
int count = 0;
VertexAttribSetup(&attrs[count++], DEC_FLOAT_4, 0, PspAttributeLocation::POSITION);
VertexAttribSetup(&attrs[count++], DEC_FLOAT_3, 16, PspAttributeLocation::TEXCOORD);
VertexAttribSetup(&attrs[count++], DEC_U8_4, 28, PspAttributeLocation::COLOR0);
VertexAttribSetup(&attrs[count++], DEC_U8_4, 32, PspAttributeLocation::COLOR1);
return count;
}
static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pipelineCache,
VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &key,
const VertexDecoder *vtxDec, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) {
VkPipelineColorBlendAttachmentState blend0 = {};
blend0.blendEnable = key.blendEnable;
if (key.blendEnable) {
blend0.colorBlendOp = (VkBlendOp)key.blendOpColor;
blend0.alphaBlendOp = (VkBlendOp)key.blendOpAlpha;
blend0.srcColorBlendFactor = (VkBlendFactor)key.srcColor;
blend0.srcAlphaBlendFactor = (VkBlendFactor)key.srcAlpha;
blend0.dstColorBlendFactor = (VkBlendFactor)key.destColor;
blend0.dstAlphaBlendFactor = (VkBlendFactor)key.destAlpha;
}
blend0.colorWriteMask = key.colorWriteMask;
VkPipelineColorBlendStateCreateInfo cbs = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
cbs.flags = 0;
cbs.pAttachments = &blend0;
cbs.attachmentCount = 1;
cbs.logicOpEnable = key.logicOpEnable;
if (key.logicOpEnable)
cbs.logicOp = (VkLogicOp)key.logicOp;
else
cbs.logicOp = VK_LOGIC_OP_COPY;
VkPipelineDepthStencilStateCreateInfo dss = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
dss.depthBoundsTestEnable = false;
dss.stencilTestEnable = key.stencilTestEnable;
if (key.stencilTestEnable) {
dss.front.compareOp = (VkCompareOp)key.stencilCompareOp;
dss.front.passOp = (VkStencilOp)key.stencilPassOp;
dss.front.failOp = (VkStencilOp)key.stencilFailOp;
dss.front.depthFailOp = (VkStencilOp)key.stencilDepthFailOp;
// Back stencil is always the same as front on PSP.
memcpy(&dss.back, &dss.front, sizeof(dss.front));
}
dss.depthTestEnable = key.depthTestEnable;
if (key.depthTestEnable) {
dss.depthCompareOp = (VkCompareOp)key.depthCompareOp;
dss.depthWriteEnable = key.depthWriteEnable;
}
VkDynamicState dynamicStates[8];
int numDyn = 0;
if (key.blendEnable) {
dynamicStates[numDyn++] = VK_DYNAMIC_STATE_BLEND_CONSTANTS;
}
dynamicStates[numDyn++] = VK_DYNAMIC_STATE_SCISSOR;
dynamicStates[numDyn++] = VK_DYNAMIC_STATE_VIEWPORT;
if (key.stencilTestEnable) {
dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK;
dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE;
dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_WRITE_MASK;
}
VkPipelineDynamicStateCreateInfo ds = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
ds.flags = 0;
ds.pDynamicStates = dynamicStates;
ds.dynamicStateCount = numDyn;
VkPipelineRasterizationStateCreateInfo rs = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
rs.flags = 0;
rs.depthBiasEnable = false;
rs.cullMode = key.cullMode;
rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rs.lineWidth = 1.0f;
rs.rasterizerDiscardEnable = false;
rs.polygonMode = VK_POLYGON_MODE_FILL;
rs.depthClampEnable = false;
VkPipelineMultisampleStateCreateInfo ms = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
ms.pSampleMask = nullptr;
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineShaderStageCreateInfo ss[2];
ss[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
ss[0].pNext = nullptr;
ss[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
ss[0].pSpecializationInfo = nullptr;
ss[0].module = vs->GetModule();
ss[0].pName = "main";
ss[0].flags = 0;
ss[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
ss[1].pNext = nullptr;
ss[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
ss[1].pSpecializationInfo = nullptr;
ss[1].module = fs->GetModule();
ss[1].pName = "main";
ss[1].flags = 0;
if (!ss[0].module || !ss[1].module) {
ERROR_LOG(G3D, "Failed creating graphics pipeline - bad shaders");
return nullptr;
}
VkPipelineInputAssemblyStateCreateInfo inputAssembly = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
inputAssembly.flags = 0;
inputAssembly.topology = (VkPrimitiveTopology)key.topology;
inputAssembly.primitiveRestartEnable = false;
int vertexStride = 0;
int offset = 0;
VkVertexInputAttributeDescription attrs[8];
int attributeCount;
if (useHwTransform) {
attributeCount = SetupVertexAttribs(attrs, vtxDec->decFmt);
vertexStride = vtxDec->decFmt.stride;
} else {
attributeCount = SetupVertexAttribsPretransformed(attrs, vtxDec->decFmt);
vertexStride = 36;
}
VkVertexInputBindingDescription ibd;
ibd.binding = 0;
ibd.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
ibd.stride = vertexStride;
VkPipelineVertexInputStateCreateInfo vis = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
vis.flags = 0;
vis.vertexBindingDescriptionCount = 1;
vis.pVertexBindingDescriptions = &ibd;
vis.vertexAttributeDescriptionCount = attributeCount;
vis.pVertexAttributeDescriptions = attrs;
VkPipelineViewportStateCreateInfo views = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
views.flags = 0;
views.viewportCount = 1;
views.scissorCount = 1;
views.pViewports = nullptr; // dynamic
views.pScissors = nullptr; // dynamic
VkGraphicsPipelineCreateInfo pipe = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
pipe.flags = 0;
pipe.stageCount = 2;
pipe.pStages = ss;
pipe.basePipelineIndex = 0;
pipe.pColorBlendState = &cbs;
pipe.pDepthStencilState = &dss;
pipe.pRasterizationState = &rs;
// We will use dynamic viewport state.
pipe.pVertexInputState = &vis;
pipe.pViewportState = &views;
pipe.pTessellationState = nullptr;
pipe.pDynamicState = &ds;
pipe.pInputAssemblyState = &inputAssembly;
pipe.pMultisampleState = &ms;
pipe.layout = layout;
pipe.basePipelineHandle = VK_NULL_HANDLE;
pipe.basePipelineIndex = 0;
pipe.renderPass = renderPass;
pipe.subpass = 0;
VkPipeline pipeline;
VkResult result = vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipe, nullptr, &pipeline);
if (result != VK_SUCCESS) {
ERROR_LOG(G3D, "Failed creating graphics pipeline!");
return nullptr;
}
VulkanPipeline *vulkanPipeline = new VulkanPipeline();
vulkanPipeline->pipeline = pipeline;
vulkanPipeline->uniformBlocks = UB_VS_FS_BASE;
if (useHwTransform) {
if (vs->HasLights()) {
vulkanPipeline->uniformBlocks |= UB_VS_LIGHTS;
}
if (vs->HasBones()) {
vulkanPipeline->uniformBlocks |= UB_VS_BONES;
}
}
return vulkanPipeline;
}
VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const VertexDecoder *vtxDec, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) {
VulkanPipelineKey key;
key.raster = rasterKey;
key.useHWTransform = useHwTransform;
key.vShader = vs->GetModule();
key.fShader = fs->GetModule();
key.vtxDec = useHwTransform ? vtxDec : nullptr;
auto iter = pipelines_.find(key);
if (iter != pipelines_.end()) {
return iter->second;
}
VulkanPipeline *pipeline = CreateVulkanPipeline(
vulkan_->GetDevice(), pipelineCache_, layout, vulkan_->GetSurfaceRenderPass(),
rasterKey, vtxDec, vs, fs, useHwTransform);
pipelines_[key] = pipeline;
return pipeline;
}
std::vector<std::string> PipelineManagerVulkan::DebugGetObjectIDs(DebugShaderType type) {
std::string id;
std::vector<std::string> ids;
switch (type) {
case SHADER_TYPE_PIPELINE:
{
for (auto iter : pipelines_) {
iter.first.ToString(&id);
ids.push_back(id);
}
}
break;
default:
break;
}
return ids;
}
std::string PipelineManagerVulkan::DebugGetObjectString(std::string id, DebugShaderType type, DebugShaderStringType stringType) {
if (type != SHADER_TYPE_PIPELINE)
return "N/A";
VulkanPipelineKey shaderId;
shaderId.FromString(id);
auto iter = pipelines_.find(shaderId);
if (iter == pipelines_.end()) {
return "";
}
switch (stringType) {
case SHADER_STRING_SHORT_DESC:
{
return StringFromFormat("%p", &iter->second);
}
case SHADER_STRING_SOURCE_CODE:
{
return "N/A";
}
default:
return "N/A";
}
}

View File

@ -0,0 +1,97 @@
// Copyright (c) 2015- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include <map>
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/ShaderId.h"
#include "GPU/Vulkan/VulkanUtil.h"
#include "GPU/Vulkan/StateMappingVulkan.h"
// PSP vertex format.
enum class PspAttributeLocation {
POSITION = 0,
TEXCOORD = 1,
NORMAL = 2,
W1 = 3,
W2 = 4,
COLOR0 = 5,
COLOR1 = 6,
COUNT
};
struct VulkanPipelineKey {
VulkanPipelineRasterStateKey raster; // prim is included here
bool useHWTransform;
const VertexDecoder *vtxDec;
VkShaderModule vShader;
VkShaderModule fShader;
bool operator < (const VulkanPipelineKey &other) const {
if (raster < other.raster) return true; else if (other.raster < raster) return false;
if (useHWTransform < other.useHWTransform) return true; else if (other.useHWTransform < useHWTransform) return false;
if (vtxDec < other.vtxDec) return true; else if (other.vtxDec < vtxDec) return false;
if (vShader < other.vShader) return true; else if (other.vShader < vShader) return false;
if (fShader < other.fShader) return true; else if (other.fShader < fShader) return false;
return false;
}
void ToString(std::string *str) const {
str->resize(sizeof(*this));
memcpy(&(*str)[0], this, sizeof(*this));
}
void FromString(const std::string &str) {
memcpy(this, &str[0], sizeof(*this));
}
};
enum {
UB_VS_FS_BASE = (1 << 0),
UB_VS_BONES = (1 << 1),
UB_VS_LIGHTS = (1 << 2),
};
// Simply wraps a Vulkan pipeline, providing some metadata.
struct VulkanPipeline {
VkPipeline pipeline;
int uniformBlocks; // UB_ enum above.
};
class VulkanContext;
class VulkanVertexShader;
class VulkanFragmentShader;
class PipelineManagerVulkan {
public:
PipelineManagerVulkan(VulkanContext *ctx);
~PipelineManagerVulkan();
VulkanPipeline *GetOrCreatePipeline(VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const VertexDecoder *vtxDec, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform);
int GetNumPipelines() const { return (int)pipelines_.size(); }
void Clear();
std::string DebugGetObjectString(std::string id, DebugShaderType type, DebugShaderStringType stringType);
std::vector<std::string> DebugGetObjectIDs(DebugShaderType type);
private:
std::map<VulkanPipelineKey, VulkanPipeline *> pipelines_;
VkPipelineCache pipelineCache_;
VulkanContext *vulkan_;
};

View File

@ -0,0 +1,577 @@
// Copyright (c) 2015- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#ifdef _WIN32
#define SHADERLOG
#endif
#include <map>
#include "base/logging.h"
#include "math/lin/matrix4x4.h"
#include "math/math_util.h"
#include "math/dataconv.h"
#include "util/text/utf8.h"
#include "Common/Vulkan/VulkanContext.h"
#include "Common/Vulkan/VulkanMemory.h"
#include "Common/Common.h"
#include "Core/Config.h"
#include "Core/Reporting.h"
#include "GPU/Math3D.h"
#include "GPU/GPUState.h"
#include "GPU/ge_constants.h"
#include "GPU/Vulkan/ShaderManagerVulkan.h"
#include "GPU/Vulkan/DrawEngineVulkan.h"
#include "GPU/Vulkan/FramebufferVulkan.h"
#include "GPU/Vulkan/FragmentShaderGeneratorVulkan.h"
#include "GPU/Vulkan/VertexShaderGeneratorVulkan.h"
#include "UI/OnScreenDisplay.h"
VulkanFragmentShader::VulkanFragmentShader(VulkanContext *vulkan, ShaderID id, const char *code, bool useHWTransform)
: vulkan_(vulkan), id_(id), failed_(false), useHWTransform_(useHWTransform), module_(0) {
source_ = code;
std::string errorMessage;
std::vector<uint32_t> spirv;
#ifdef SHADERLOG
OutputDebugStringA(code);
#endif
bool success = GLSLtoSPV(VK_SHADER_STAGE_FRAGMENT_BIT, code, spirv, &errorMessage);
if (!errorMessage.empty()) {
if (success) {
ERROR_LOG(G3D, "Warnings in shader compilation!");
} else {
ERROR_LOG(G3D, "Error in shader compilation!");
}
ERROR_LOG(G3D, "Messages: %s", errorMessage.c_str());
ERROR_LOG(G3D, "Shader source:\n%s", code);
#ifdef SHADERLOG
OutputDebugStringA("Messages:\n");
OutputDebugStringA(errorMessage.c_str());
OutputDebugStringA(code);
#endif
Reporting::ReportMessage("Vulkan error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code);
} else {
success = vulkan_->CreateShaderModule(spirv, &module_);
#ifdef SHADERLOG
OutputDebugStringA("OK\n");
#endif
}
if (!success) {
failed_ = true;
return;
} else {
DEBUG_LOG(G3D, "Compiled shader:\n%s\n", (const char *)code);
}
}
VulkanFragmentShader::~VulkanFragmentShader() {
if (module_) {
vulkan_->Delete().QueueDeleteShaderModule(module_);
}
}
std::string VulkanFragmentShader::GetShaderString(DebugShaderStringType type) const {
switch (type) {
case SHADER_STRING_SOURCE_CODE:
return source_;
case SHADER_STRING_SHORT_DESC:
return FragmentShaderDesc(id_);
default:
return "N/A";
}
}
VulkanVertexShader::VulkanVertexShader(VulkanContext *vulkan, ShaderID id, const char *code, int vertType, bool useHWTransform, bool usesLighting)
: vulkan_(vulkan), id_(id), failed_(false), useHWTransform_(useHWTransform), module_(VK_NULL_HANDLE), usesLighting_(usesLighting) {
source_ = code;
std::string errorMessage;
std::vector<uint32_t> spirv;
#ifdef SHADERLOG
OutputDebugStringA(code);
#endif
bool success = GLSLtoSPV(VK_SHADER_STAGE_VERTEX_BIT, code, spirv, &errorMessage);
if (!errorMessage.empty()) {
if (success) {
ERROR_LOG(G3D, "Warnings in shader compilation!");
} else {
ERROR_LOG(G3D, "Error in shader compilation!");
}
ERROR_LOG(G3D, "Messages: %s", errorMessage.c_str());
ERROR_LOG(G3D, "Shader source:\n%s", code);
OutputDebugStringUTF8("Messages:\n");
OutputDebugStringUTF8(errorMessage.c_str());
Reporting::ReportMessage("Vulkan error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code);
} else {
success = vulkan_->CreateShaderModule(spirv, &module_);
#ifdef SHADERLOG
OutputDebugStringA("OK\n");
#endif
}
if (!success) {
failed_ = true;
module_ = VK_NULL_HANDLE;
return;
} else {
DEBUG_LOG(G3D, "Compiled shader:\n%s\n", (const char *)code);
}
}
VulkanVertexShader::~VulkanVertexShader() {
if (module_) {
vulkan_->Delete().QueueDeleteShaderModule(module_);
}
}
std::string VulkanVertexShader::GetShaderString(DebugShaderStringType type) const {
switch (type) {
case SHADER_STRING_SOURCE_CODE:
return source_;
case SHADER_STRING_SHORT_DESC:
return VertexShaderDesc(id_);
default:
return "N/A";
}
}
static void ConvertProjMatrixToVulkan(Matrix4x4 &in, bool invertedX, bool invertedY) {
const Vec3 trans(0, 0, gstate_c.vpZOffset * 0.5f + 0.5f);
const Vec3 scale(gstate_c.vpWidthScale, gstate_c.vpHeightScale, gstate_c.vpDepthScale * 0.5f);
in.translateAndScale(trans, scale);
}
static void ConvertProjMatrixToVulkanThrough(Matrix4x4 &in) {
in.translateAndScale(Vec3(0.0f, 0.0f, 0.5f), Vec3(1.0f, 1.0f, 0.5f));
}
ShaderManagerVulkan::ShaderManagerVulkan(VulkanContext *vulkan)
: vulkan_(vulkan), lastVShader_(nullptr), lastFShader_(nullptr), globalDirty_(0xFFFFFFFF) {
codeBuffer_ = new char[16384];
uboAlignment_ = vulkan_->GetPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment;
memset(&ub_base, 0, sizeof(ub_base));
memset(&ub_lights, 0, sizeof(ub_lights));
memset(&ub_bones, 0, sizeof(ub_bones));
ILOG("sizeof(ub_base): %d", (int)sizeof(ub_base));
ILOG("sizeof(ub_lights): %d", (int)sizeof(ub_lights));
ILOG("sizeof(ub_bones): %d", (int)sizeof(ub_bones));
}
ShaderManagerVulkan::~ShaderManagerVulkan() {
ClearShaders();
delete[] codeBuffer_;
}
uint32_t ShaderManagerVulkan::PushBaseBuffer(VulkanPushBuffer *dest, VkBuffer *buf) {
return dest->PushAligned(&ub_base, sizeof(ub_base), uboAlignment_, buf);
}
uint32_t ShaderManagerVulkan::PushLightBuffer(VulkanPushBuffer *dest, VkBuffer *buf) {
return dest->PushAligned(&ub_lights, sizeof(ub_lights), uboAlignment_, buf);
}
// TODO: Only push half the bone buffer if we only have four bones.
uint32_t ShaderManagerVulkan::PushBoneBuffer(VulkanPushBuffer *dest, VkBuffer *buf) {
return dest->PushAligned(&ub_bones, sizeof(ub_bones), uboAlignment_, buf);
}
void ShaderManagerVulkan::BaseUpdateUniforms(int dirtyUniforms) {
if (dirtyUniforms & DIRTY_TEXENV) {
Uint8x3ToFloat4(ub_base.texEnvColor, gstate.texenvcolor);
}
if (dirtyUniforms & DIRTY_ALPHACOLORREF) {
Uint8x3ToInt4_Alpha(ub_base.alphaColorRef, gstate.getColorTestRef(), gstate.getAlphaTestRef() & gstate.getAlphaTestMask());
}
if (dirtyUniforms & DIRTY_ALPHACOLORMASK) {
Uint8x3ToInt4_Alpha(ub_base.colorTestMask, gstate.getColorTestMask(), gstate.getAlphaTestMask());
}
if (dirtyUniforms & DIRTY_FOGCOLOR) {
Uint8x3ToFloat4(ub_base.fogColor, gstate.fogcolor);
}
if (dirtyUniforms & DIRTY_SHADERBLEND) {
Uint8x3ToFloat4(ub_base.blendFixA, gstate.getFixA());
Uint8x3ToFloat4(ub_base.blendFixB, gstate.getFixB());
}
if (dirtyUniforms & DIRTY_TEXCLAMP) {
const float invW = 1.0f / (float)gstate_c.curTextureWidth;
const float invH = 1.0f / (float)gstate_c.curTextureHeight;
const int w = gstate.getTextureWidth(0);
const int h = gstate.getTextureHeight(0);
const float widthFactor = (float)w * invW;
const float heightFactor = (float)h * invH;
// First wrap xy, then half texel xy (for clamp.)
const float texclamp[4] = {
widthFactor,
heightFactor,
invW * 0.5f,
invH * 0.5f,
};
const float texclampoff[2] = {
gstate_c.curTextureXOffset * invW,
gstate_c.curTextureYOffset * invH,
};
CopyFloat4(ub_base.texClamp, texclamp);
CopyFloat2(ub_base.texClampOffset, texclampoff);
}
if (dirtyUniforms & DIRTY_PROJMATRIX) {
Matrix4x4 flippedMatrix;
memcpy(&flippedMatrix, gstate.projMatrix, 16 * sizeof(float));
const bool invertedY = gstate_c.vpHeight < 0;
if (invertedY) {
flippedMatrix[1] = -flippedMatrix[1];
flippedMatrix[5] = -flippedMatrix[5];
flippedMatrix[9] = -flippedMatrix[9];
flippedMatrix[13] = -flippedMatrix[13];
}
const bool invertedX = gstate_c.vpWidth < 0;
if (invertedX) {
flippedMatrix[0] = -flippedMatrix[0];
flippedMatrix[4] = -flippedMatrix[4];
flippedMatrix[8] = -flippedMatrix[8];
flippedMatrix[12] = -flippedMatrix[12];
}
ConvertProjMatrixToVulkan(flippedMatrix, invertedX, invertedY);
CopyMatrix4x4(ub_base.proj, flippedMatrix.getReadPtr());
}
if (dirtyUniforms & DIRTY_PROJTHROUGHMATRIX) {
Matrix4x4 proj_through;
proj_through.setOrthoVulkan(0.0f, gstate_c.curRTWidth, 0, gstate_c.curRTHeight, 0, 1);
CopyMatrix4x4(ub_base.proj_through, proj_through.getReadPtr());
}
// Transform
if (dirtyUniforms & DIRTY_WORLDMATRIX) {
ConvertMatrix4x3To4x4(ub_base.world, gstate.worldMatrix);
}
if (dirtyUniforms & DIRTY_VIEWMATRIX) {
ConvertMatrix4x3To4x4(ub_base.view, gstate.viewMatrix);
}
if (dirtyUniforms & DIRTY_TEXMATRIX) {
ConvertMatrix4x3To4x4(ub_base.tex, gstate.tgenMatrix);
}
// Combined two small uniforms
if (dirtyUniforms & (DIRTY_FOGCOEF | DIRTY_STENCILREPLACEVALUE)) {
float fogcoef_stencil[3] = {
getFloat24(gstate.fog1),
getFloat24(gstate.fog2),
(float)gstate.getStencilTestRef()
};
if (my_isinf(fogcoef_stencil[1])) {
// not really sure what a sensible value might be.
fogcoef_stencil[1] = fogcoef_stencil[1] < 0.0f ? -10000.0f : 10000.0f;
} else if (my_isnan(fogcoef_stencil[1])) {
// Workaround for https://github.com/hrydgard/ppsspp/issues/5384#issuecomment-38365988
// Just put the fog far away at a large finite distance.
// Infinities and NaNs are rather unpredictable in shaders on many GPUs
// so it's best to just make it a sane calculation.
fogcoef_stencil[0] = 100000.0f;
fogcoef_stencil[1] = 1.0f;
}
#ifndef MOBILE_DEVICE
else if (my_isnanorinf(fogcoef_stencil[1]) || my_isnanorinf(fogcoef_stencil[0])) {
ERROR_LOG_REPORT_ONCE(fognan, G3D, "Unhandled fog NaN/INF combo: %f %f", fogcoef_stencil[0], fogcoef_stencil[1]);
}
#endif
CopyFloat3(ub_base.fogCoef_stencil, fogcoef_stencil);
}
// Texturing
if (dirtyUniforms & DIRTY_UVSCALEOFFSET) {
const float invW = 1.0f / (float)gstate_c.curTextureWidth;
const float invH = 1.0f / (float)gstate_c.curTextureHeight;
const int w = gstate.getTextureWidth(0);
const int h = gstate.getTextureHeight(0);
const float widthFactor = (float)w * invW;
const float heightFactor = (float)h * invH;
static const float rescale[4] = { 1.0f, 2 * 127.5f / 128.f, 2 * 32767.5f / 32768.f, 1.0f };
const float factor = rescale[(gstate.vertType & GE_VTYPE_TC_MASK) >> GE_VTYPE_TC_SHIFT];
float uvscaleoff[4];
switch (gstate.getUVGenMode()) {
case GE_TEXMAP_TEXTURE_COORDS:
// Not sure what GE_TEXMAP_UNKNOWN is, but seen in Riviera. Treating the same as GE_TEXMAP_TEXTURE_COORDS works.
case GE_TEXMAP_UNKNOWN:
uvscaleoff[0] = gstate_c.uv.uScale * factor * widthFactor;
uvscaleoff[1] = gstate_c.uv.vScale * factor * heightFactor;
uvscaleoff[2] = gstate_c.uv.uOff * widthFactor;
uvscaleoff[3] = gstate_c.uv.vOff * heightFactor;
break;
// These two work the same whether or not we prescale UV.
case GE_TEXMAP_TEXTURE_MATRIX:
// We cannot bake the UV coord scale factor in here, as we apply a matrix multiplication
// before this is applied, and the matrix multiplication may contain translation. In this case
// the translation will be scaled which breaks faces in Hexyz Force for example.
// So I've gone back to applying the scale factor in the shader.
uvscaleoff[0] = widthFactor;
uvscaleoff[1] = heightFactor;
uvscaleoff[2] = 0.0f;
uvscaleoff[3] = 0.0f;
break;
case GE_TEXMAP_ENVIRONMENT_MAP:
// In this mode we only use uvscaleoff to scale to the texture size.
uvscaleoff[0] = widthFactor;
uvscaleoff[1] = heightFactor;
uvscaleoff[2] = 0.0f;
uvscaleoff[3] = 0.0f;
break;
default:
ERROR_LOG_REPORT(G3D, "Unexpected UV gen mode: %d", gstate.getUVGenMode());
}
CopyFloat4(ub_base.uvScaleOffset, uvscaleoff);
}
if (dirtyUniforms & DIRTY_DEPTHRANGE) {
float viewZScale = gstate.getViewportZScale();
float viewZCenter = gstate.getViewportZCenter();
float viewZInvScale;
// We had to scale and translate Z to account for our clamped Z range.
// Therefore, we also need to reverse this to round properly.
//
// Example: scale = 65535.0, center = 0.0
// Resulting range = -65535 to 65535, clamped to [0, 65535]
// gstate_c.vpDepthScale = 2.0f
// gstate_c.vpZOffset = -1.0f
//
// The projection already accounts for those, so we need to reverse them.
//
// Additionally, D3D9 uses a range from [0, 1]. We double and move the center.
viewZScale *= (1.0f / gstate_c.vpDepthScale) * 2.0f;
viewZCenter -= 65535.0f * gstate_c.vpZOffset + 32768.5f;
if (viewZScale != 0.0) {
viewZInvScale = 1.0f / viewZScale;
} else {
viewZInvScale = 0.0;
}
float data[4] = { viewZScale, viewZCenter, viewZCenter, viewZInvScale };
CopyFloat4(ub_base.depthRange, data);
}
}
void ShaderManagerVulkan::LightUpdateUniforms(int dirtyUniforms) {
// Lighting
if (dirtyUniforms & DIRTY_AMBIENT) {
Uint8x3ToFloat4_AlphaUint8(ub_lights.ambientColor, gstate.ambientcolor, gstate.getAmbientA());
}
if (dirtyUniforms & DIRTY_MATAMBIENTALPHA) {
// Note - this one is not in lighting but in transformCommon as it has uses beyond lighting
Uint8x3ToFloat4_AlphaUint8(ub_base.matAmbient, gstate.materialambient, gstate.getMaterialAmbientA());
}
if (dirtyUniforms & DIRTY_MATDIFFUSE) {
Uint8x3ToFloat4(ub_lights.materialDiffuse, gstate.materialdiffuse);
}
if (dirtyUniforms & DIRTY_MATEMISSIVE) {
Uint8x3ToFloat4(ub_lights.materialEmissive, gstate.materialemissive);
}
if (dirtyUniforms & DIRTY_MATSPECULAR) {
Uint8x3ToFloat4_Alpha(ub_lights.materialSpecular, gstate.materialspecular, getFloat24(gstate.materialspecularcoef));
}
for (int i = 0; i < 4; i++) {
if (dirtyUniforms & (DIRTY_LIGHT0 << i)) {
if (gstate.isDirectionalLight(i)) {
// Prenormalize
float x = getFloat24(gstate.lpos[i * 3 + 0]);
float y = getFloat24(gstate.lpos[i * 3 + 1]);
float z = getFloat24(gstate.lpos[i * 3 + 2]);
float len = sqrtf(x*x + y*y + z*z);
if (len == 0.0f)
len = 1.0f;
else
len = 1.0f / len;
float vec[3] = { x * len, y * len, z * len };
CopyFloat3To4(ub_lights.lpos[i], vec);
} else {
ExpandFloat24x3ToFloat4(ub_lights.lpos[i], &gstate.lpos[i * 3]);
}
ExpandFloat24x3ToFloat4(ub_lights.ldir[i], &gstate.ldir[i * 3]);
ExpandFloat24x3ToFloat4(ub_lights.latt[i], &gstate.latt[i * 3]);
CopyFloat1To4(ub_lights.lightAngle[i], getFloat24(gstate.lcutoff[i]));
CopyFloat1To4(ub_lights.lightSpotCoef[i], getFloat24(gstate.lconv[i]));
Uint8x3ToFloat4(ub_lights.lightAmbient[i], gstate.lcolor[i * 3]);
Uint8x3ToFloat4(ub_lights.lightDiffuse[i], gstate.lcolor[i * 3 + 1]);
Uint8x3ToFloat4(ub_lights.lightSpecular[i], gstate.lcolor[i * 3 + 2]);
}
}
}
void ShaderManagerVulkan::BoneUpdateUniforms(int dirtyUniforms) {
for (int i = 0; i < 8; i++) {
if (dirtyUniforms & (DIRTY_BONEMATRIX0 << i)) {
ConvertMatrix4x3To4x4(ub_bones.bones[i], gstate.boneMatrix + 12 * i);
}
}
}
void ShaderManagerVulkan::Clear() {
for (auto iter = fsCache_.begin(); iter != fsCache_.end(); ++iter) {
delete iter->second;
}
for (auto iter = vsCache_.begin(); iter != vsCache_.end(); ++iter) {
delete iter->second;
}
fsCache_.clear();
vsCache_.clear();
lastFSID_.clear();
lastVSID_.clear();
}
void ShaderManagerVulkan::ClearShaders() {
Clear();
DirtyShader();
DirtyUniform(0xFFFFFFFF);
}
void ShaderManagerVulkan::DirtyShader() {
// Forget the last shader ID
lastFSID_.clear();
lastVSID_.clear();
lastVShader_ = nullptr;
lastFShader_ = nullptr;
}
void ShaderManagerVulkan::DirtyLastShader() { // disables vertex arrays
lastVShader_ = nullptr;
lastFShader_ = nullptr;
}
uint32_t ShaderManagerVulkan::UpdateUniforms() {
uint32_t dirty = globalDirty_;
if (globalDirty_) {
BaseUpdateUniforms(dirty);
LightUpdateUniforms(dirty);
BoneUpdateUniforms(dirty);
}
globalDirty_ = 0;
return dirty;
}
void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, bool useHWTransform) {
ShaderID VSID;
ShaderID FSID;
ComputeVertexShaderID(&VSID, vertType, useHWTransform);
ComputeFragmentShaderID(&FSID);
// Just update uniforms if this is the same shader as last time.
if (lastVShader_ != nullptr && lastFShader_ != nullptr && VSID == lastVSID_ && FSID == lastFSID_) {
*vshader = lastVShader_;
*fshader = lastFShader_;
// Already all set, no need to look up in shader maps.
return;
}
VSCache::iterator vsIter = vsCache_.find(VSID);
VulkanVertexShader *vs;
if (vsIter == vsCache_.end()) {
// Vertex shader not in cache. Let's compile it.
bool usesLighting;
GenerateVulkanGLSLVertexShader(VSID, codeBuffer_, &usesLighting);
vs = new VulkanVertexShader(vulkan_, VSID, codeBuffer_, vertType, useHWTransform, usesLighting);
vsCache_[VSID] = vs;
} else {
vs = vsIter->second;
}
lastVSID_ = VSID;
FSCache::iterator fsIter = fsCache_.find(FSID);
VulkanFragmentShader *fs;
if (fsIter == fsCache_.end()) {
// Fragment shader not in cache. Let's compile it.
GenerateVulkanGLSLFragmentShader(FSID, codeBuffer_);
fs = new VulkanFragmentShader(vulkan_, FSID, codeBuffer_, useHWTransform);
fsCache_[FSID] = fs;
} else {
fs = fsIter->second;
}
lastFSID_ = FSID;
lastVShader_ = vs;
lastFShader_ = fs;
*vshader = vs;
*fshader = fs;
}
std::vector<std::string> ShaderManagerVulkan::DebugGetShaderIDs(DebugShaderType type) {
std::string id;
std::vector<std::string> ids;
switch (type) {
case SHADER_TYPE_VERTEX:
{
for (auto iter : vsCache_) {
iter.first.ToString(&id);
ids.push_back(id);
}
break;
}
case SHADER_TYPE_FRAGMENT:
{
for (auto iter : fsCache_) {
iter.first.ToString(&id);
ids.push_back(id);
}
break;
}
default:
break;
}
return ids;
}
std::string ShaderManagerVulkan::DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType) {
ShaderID shaderId;
shaderId.FromString(id);
switch (type) {
case SHADER_TYPE_VERTEX:
{
auto iter = vsCache_.find(shaderId);
if (iter == vsCache_.end()) {
return "";
}
return iter->second->GetShaderString(stringType);
}
case SHADER_TYPE_FRAGMENT:
{
auto iter = fsCache_.find(shaderId);
if (iter == fsCache_.end()) {
return "";
}
return iter->second->GetShaderString(stringType);
}
default:
return "N/A";
}
}

View File

@ -0,0 +1,288 @@
// Copyright (c) 2016- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include <map>
#include "base/basictypes.h"
#include "Globals.h"
#include "GPU/Common/ShaderCommon.h"
#include "GPU/Common/ShaderId.h"
#include "GPU/Vulkan/VertexShaderGeneratorVulkan.h"
#include "GPU/Vulkan/FragmentShaderGeneratorVulkan.h"
#include "GPU/Vulkan/VulkanUtil.h"
#include "math/lin/matrix4x4.h"
void ConvertProjMatrixToVulkan(Matrix4x4 & in);
// Pretty much full. Will need more bits for more fine grained dirty tracking for lights.
enum {
DIRTY_PROJMATRIX = (1 << 0),
DIRTY_PROJTHROUGHMATRIX = (1 << 1),
DIRTY_FOGCOLOR = (1 << 2),
DIRTY_FOGCOEF = (1 << 3),
DIRTY_TEXENV = (1 << 4),
DIRTY_ALPHACOLORREF = (1 << 5),
DIRTY_STENCILREPLACEVALUE = (1 << 6),
DIRTY_ALPHACOLORMASK = (1 << 7),
DIRTY_LIGHT0 = (1 << 8),
DIRTY_LIGHT1 = (1 << 9),
DIRTY_LIGHT2 = (1 << 10),
DIRTY_LIGHT3 = (1 << 11),
DIRTY_MATDIFFUSE = (1 << 12),
DIRTY_MATSPECULAR = (1 << 13),
DIRTY_MATEMISSIVE = (1 << 14),
DIRTY_AMBIENT = (1 << 15),
DIRTY_MATAMBIENTALPHA = (1 << 16),
DIRTY_SHADERBLEND = (1 << 17), // Used only for in-shader blending.
DIRTY_UVSCALEOFFSET = (1 << 18), // this will be dirtied ALL THE TIME... maybe we'll need to do "last value with this shader compares"
DIRTY_TEXCLAMP = (1 << 19),
DIRTY_DEPTHRANGE = (1 << 20),
DIRTY_WORLDMATRIX = (1 << 21),
DIRTY_VIEWMATRIX = (1 << 22),
DIRTY_TEXMATRIX = (1 << 23),
DIRTY_BONEMATRIX0 = (1 << 24),
DIRTY_BONEMATRIX1 = (1 << 25),
DIRTY_BONEMATRIX2 = (1 << 26),
DIRTY_BONEMATRIX3 = (1 << 27),
DIRTY_BONEMATRIX4 = (1 << 28),
DIRTY_BONEMATRIX5 = (1 << 29),
DIRTY_BONEMATRIX6 = (1 << 30),
DIRTY_BONEMATRIX7 = (1 << 31),
DIRTY_BASE_UNIFORMS =
DIRTY_WORLDMATRIX | DIRTY_PROJTHROUGHMATRIX | DIRTY_VIEWMATRIX | DIRTY_TEXMATRIX | DIRTY_ALPHACOLORREF |
DIRTY_PROJMATRIX | DIRTY_FOGCOLOR | DIRTY_FOGCOEF | DIRTY_TEXENV | DIRTY_STENCILREPLACEVALUE |
DIRTY_ALPHACOLORMASK | DIRTY_SHADERBLEND | DIRTY_UVSCALEOFFSET | DIRTY_TEXCLAMP | DIRTY_DEPTHRANGE | DIRTY_MATAMBIENTALPHA,
DIRTY_LIGHT_UNIFORMS =
DIRTY_LIGHT0 | DIRTY_LIGHT1 | DIRTY_LIGHT2 | DIRTY_LIGHT3 |
DIRTY_MATDIFFUSE | DIRTY_MATSPECULAR | DIRTY_MATEMISSIVE | DIRTY_AMBIENT,
DIRTY_BONE_UNIFORMS = 0xFF000000,
DIRTY_ALL = 0xFFFFFFFF
};
// TODO: Split into two structs, one for software transform and one for hardware transform, to save space.
// 512 bytes. Probably can't get to 256 (nVidia's UBO alignment).
struct UB_VS_FS_Base {
float proj[16];
float proj_through[16];
float view[16];
float world[16];
float tex[16]; // not that common, may want to break out
float uvScaleOffset[4];
float depthRange[4];
float fogCoef_stencil[4];
float matAmbient[4];
// Fragment data
float fogColor[4];
float texEnvColor[4];
int alphaColorRef[4];
int colorTestMask[4];
float blendFixA[4];
float blendFixB[4];
float texClamp[4];
float texClampOffset[4];
};
static const char *ub_baseStr =
R"( mat4 proj_mtx;
mat4 proj_through_mtx;
mat4 view_mtx;
mat4 world_mtx;
mat4 tex_mtx;
vec4 uvscaleoffset;
vec4 depthRange;
vec3 fogcoef_stencilreplace;
vec4 matambientalpha;
vec3 fogcolor;
vec3 texenv;
ivec4 alphacolorref;
ivec4 alphacolormask;
vec3 blendFixA;
vec3 blendFixB;
vec4 texclamp;
vec2 texclampoff;
)";
// 576 bytes. Can we get down to 512?
struct UB_VS_Lights {
float ambientColor[4];
float materialDiffuse[4];
float materialSpecular[4];
float materialEmissive[4];
float lpos[4][4];
float ldir[4][4];
float latt[4][4];
float lightAngle[4][4]; // TODO: Merge with lightSpotCoef, use .xy
float lightSpotCoef[4][4];
float lightAmbient[4][4];
float lightDiffuse[4][4];
float lightSpecular[4][4];
};
static const char *ub_vs_lightsStr =
R"( vec4 globalAmbient;
vec3 matdiffuse;
vec4 matspecular;
vec3 matemissive;
vec3 pos[4];
vec3 dir[4];
vec3 att[4];
float angle[4];
float spotCoef[4];
vec3 ambient[4];
vec3 diffuse[4];
vec3 specular[4];
)";
// With some cleverness, we could get away with uploading just half this when only the four first
// bones are being used. This is 512b, 256b would be great.
// Could also move to 4x3 matrices - would let us fit 5 bones into 256b.
struct UB_VS_Bones {
float bones[8][16];
};
static const char *ub_vs_bonesStr =
R"( mat4 m[8];
)";
class VulkanContext;
class VulkanPushBuffer;
class VulkanFragmentShader {
public:
VulkanFragmentShader(VulkanContext *vulkan, ShaderID id, const char *code, bool useHWTransform);
~VulkanFragmentShader();
const std::string &source() const { return source_; }
bool Failed() const { return failed_; }
bool UseHWTransform() const { return useHWTransform_; }
std::string GetShaderString(DebugShaderStringType type) const;
VkShaderModule GetModule() const { return module_; }
protected:
VkShaderModule module_;
VulkanContext *vulkan_;
std::string source_;
bool failed_;
bool useHWTransform_;
ShaderID id_;
};
class VulkanVertexShader {
public:
VulkanVertexShader(VulkanContext *vulkan, ShaderID id, const char *code, int vertType, bool useHWTransform, bool usesLighting);
~VulkanVertexShader();
const std::string &source() const { return source_; }
bool Failed() const { return failed_; }
bool UseHWTransform() const { return useHWTransform_; }
bool HasBones() const {
return id_.Bit(VS_BIT_ENABLE_BONES);
}
bool HasLights() const {
return usesLighting_;
}
std::string GetShaderString(DebugShaderStringType type) const;
VkShaderModule GetModule() const { return module_; }
protected:
VkShaderModule module_;
VulkanContext *vulkan_;
std::string source_;
bool failed_;
bool useHWTransform_;
bool usesLighting_;
ShaderID id_;
};
class VulkanPushBuffer;
class ShaderManagerVulkan {
public:
ShaderManagerVulkan(VulkanContext *vulkan);
~ShaderManagerVulkan();
void GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, bool useHWTransform);
void ClearShaders();
void DirtyShader();
void DirtyLastShader();
int GetNumVertexShaders() const { return (int)vsCache_.size(); }
int GetNumFragmentShaders() const { return (int)fsCache_.size(); }
std::vector<std::string> DebugGetShaderIDs(DebugShaderType type);
std::string DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType);
uint32_t UpdateUniforms();
void DirtyUniform(u32 what) {
globalDirty_ |= what;
}
// TODO: Avoid copying these buffers if same as last draw, can still point to it assuming we're still in the same pushbuffer.
// Applies dirty changes and copies the buffer.
bool IsBaseDirty() { return true; }
bool IsLightDirty() { return true; }
bool IsBoneDirty() { return true; }
uint32_t PushBaseBuffer(VulkanPushBuffer *dest, VkBuffer *buf);
uint32_t PushLightBuffer(VulkanPushBuffer *dest, VkBuffer *buf);
uint32_t PushBoneBuffer(VulkanPushBuffer *dest, VkBuffer *buf);
private:
void BaseUpdateUniforms(int dirtyUniforms);
void LightUpdateUniforms(int dirtyUniforms);
void BoneUpdateUniforms(int dirtyUniforms);
void Clear();
VulkanContext *vulkan_;
typedef std::map<ShaderID, VulkanFragmentShader *> FSCache;
FSCache fsCache_;
typedef std::map<ShaderID, VulkanVertexShader *> VSCache;
VSCache vsCache_;
char *codeBuffer_;
uint32_t globalDirty_;
uint32_t uboAlignment_;
// Uniform block scratchpad. These (the relevant ones) are copied to the current pushbuffer at draw time.
UB_VS_FS_Base ub_base;
UB_VS_Lights ub_lights;
UB_VS_Bones ub_bones;
VulkanFragmentShader *lastFShader_;
VulkanVertexShader *lastVShader_;
ShaderID lastFSID_;
ShaderID lastVSID_;
};

View File

@ -0,0 +1,316 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "Common/Vulkan/VulkanLoader.h"
#include "GPU/Math3D.h"
#include "GPU/GPUState.h"
#include "GPU/ge_constants.h"
#include "GPU/Common/GPUStateUtils.h"
#include "Core/System.h"
#include "Core/Config.h"
#include "Core/Reporting.h"
//#include "GPU/Vulkan/StateMappingVulkan.h"
#include "GPU/Vulkan/GPU_Vulkan.h"
#include "GPU/Vulkan/PipelineManagerVulkan.h"
#include "GPU/Vulkan/TextureCacheVulkan.h"
#include "GPU/Vulkan/FramebufferVulkan.h"
#include "GPU/Vulkan/ShaderManagerVulkan.h"
//#include "GPU/Vulkan/PixelShaderGeneratorVulkan.h"
// These tables all fit into u8s.
static const VkBlendFactor vkBlendFactorLookup[(size_t)BlendFactor::COUNT] = {
VK_BLEND_FACTOR_ZERO,
VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_SRC_COLOR,
VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR,
VK_BLEND_FACTOR_DST_COLOR,
VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR,
VK_BLEND_FACTOR_SRC_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
VK_BLEND_FACTOR_DST_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA,
VK_BLEND_FACTOR_CONSTANT_COLOR,
VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,
VK_BLEND_FACTOR_CONSTANT_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA,
VK_BLEND_FACTOR_SRC1_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA,
VK_BLEND_FACTOR_MAX_ENUM,
};
static const VkBlendOp vkBlendEqLookup[(size_t)BlendEq::COUNT] = {
VK_BLEND_OP_ADD,
VK_BLEND_OP_SUBTRACT,
VK_BLEND_OP_REVERSE_SUBTRACT,
VK_BLEND_OP_MIN,
VK_BLEND_OP_MAX,
};
static const VkCullModeFlagBits cullingMode[] = {
VK_CULL_MODE_BACK_BIT,
VK_CULL_MODE_FRONT_BIT,
};
static const VkCompareOp compareOps[] = {
VK_COMPARE_OP_NEVER,
VK_COMPARE_OP_ALWAYS,
VK_COMPARE_OP_EQUAL,
VK_COMPARE_OP_NOT_EQUAL,
VK_COMPARE_OP_LESS,
VK_COMPARE_OP_LESS_OR_EQUAL,
VK_COMPARE_OP_GREATER,
VK_COMPARE_OP_GREATER_OR_EQUAL,
};
static const VkStencilOp stencilOps[] = {
VK_STENCIL_OP_KEEP,
VK_STENCIL_OP_ZERO,
VK_STENCIL_OP_REPLACE,
VK_STENCIL_OP_INVERT,
VK_STENCIL_OP_INCREMENT_AND_CLAMP,
VK_STENCIL_OP_DECREMENT_AND_CLAMP,
VK_STENCIL_OP_KEEP, // reserved
VK_STENCIL_OP_KEEP, // reserved
};
const VkPrimitiveTopology primToVulkan[8] = {
VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // Vulkan doesn't do quads. We could do strips with restart-index though. We could also do RECT primitives in the geometry shader.
};
bool ApplyShaderBlending() {
return false;
}
void ResetShaderBlending() {
//
}
// TODO: Do this more progressively. No need to compute the entire state if the entire state hasn't changed.
// In Vulkan, we simply collect all the state together into a "pipeline key" - we don't actually set any state here
// (the caller is responsible for setting the little dynamic state that is supported, dynState).
void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState) {
memset(&key, 0, sizeof(key));
memset(&dynState, 0, sizeof(dynState));
// Unfortunately, this isn't implemented yet.
gstate_c.allowShaderBlend = false;
// Set blend - unless we need to do it in the shader.
GenericBlendState blendState;
ConvertBlendState(blendState, gstate_c.allowShaderBlend);
bool useBufferedRendering = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE;
ViewportAndScissor vpAndScissor;
ConvertViewportAndScissor(useBufferedRendering,
fbManager.GetRenderWidth(), fbManager.GetRenderHeight(),
fbManager.GetTargetBufferWidth(), fbManager.GetTargetBufferHeight(),
vpAndScissor);
if (blendState.applyShaderBlending) {
if (ApplyShaderBlending()) {
// We may still want to do something about stencil -> alpha.
ApplyStencilReplaceAndLogicOp(blendState.replaceAlphaWithStencil, blendState);
} else {
// Until next time, force it off.
ResetShaderBlending();
gstate_c.allowShaderBlend = false;
}
} else if (blendState.resetShaderBlending) {
ResetShaderBlending();
}
if (blendState.enabled) {
key.blendEnable = true;
key.blendOpColor = vkBlendEqLookup[(size_t)blendState.eqColor];
key.blendOpAlpha = vkBlendEqLookup[(size_t)blendState.eqAlpha];
key.srcColor = vkBlendFactorLookup[(size_t)blendState.srcColor];
key.srcAlpha = vkBlendFactorLookup[(size_t)blendState.srcAlpha];
key.destColor = vkBlendFactorLookup[(size_t)blendState.dstColor];
key.destAlpha = vkBlendFactorLookup[(size_t)blendState.dstAlpha];
if (blendState.dirtyShaderBlend) {
shaderManager->DirtyUniform(DIRTY_SHADERBLEND);
}
dynState.useBlendColor = blendState.useBlendColor;
if (blendState.useBlendColor) {
dynState.blendColor = blendState.blendColor;
}
} else {
key.blendEnable = false;
dynState.useBlendColor = false;
}
dynState.useStencil = false;
// Set ColorMask/Stencil/Depth
if (gstate.isModeClear()) {
key.cullMode = VK_CULL_MODE_NONE;
key.depthTestEnable = true;
key.depthCompareOp = VK_COMPARE_OP_ALWAYS;
key.depthWriteEnable = gstate.isClearModeDepthMask();
if (gstate.isClearModeDepthMask()) {
fbManager.SetDepthUpdated();
}
// Color Test
bool colorMask = gstate.isClearModeColorMask();
bool alphaMask = gstate.isClearModeAlphaMask();
key.colorWriteMask = (colorMask ? (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT) : 0) | (alphaMask ? VK_COLOR_COMPONENT_A_BIT : 0);
GenericStencilFuncState stencilState;
ConvertStencilFuncState(stencilState);
// Stencil Test
if (stencilState.enabled) {
key.stencilTestEnable = true;
key.stencilCompareOp = compareOps[stencilState.testFunc];
key.stencilPassOp = stencilOps[stencilState.zPass];
key.stencilFailOp = stencilOps[stencilState.sFail];
key.stencilDepthFailOp = stencilOps[stencilState.zFail];
dynState.useStencil = true;
dynState.stencilRef = stencilState.testRef;
dynState.stencilCompareMask = stencilState.testMask;
dynState.stencilWriteMask = stencilState.writeMask;
} else {
key.stencilTestEnable = false;
dynState.useStencil = false;
}
} else {
// Set cull
bool wantCull = !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES && gstate.isCullEnabled();
key.cullMode = wantCull ? (gstate.getCullMode() ? VK_CULL_MODE_FRONT_BIT : VK_CULL_MODE_BACK_BIT) : VK_CULL_MODE_NONE;
// Depth Test
if (gstate.isDepthTestEnabled()) {
key.depthTestEnable = true;
key.depthCompareOp = compareOps[gstate.getDepthTestFunction()];
key.depthWriteEnable = gstate.isDepthWriteEnabled();
if (gstate.isDepthWriteEnabled()) {
// framebufferManager_->SetDepthUpdated();
}
} else {
key.depthTestEnable = false;
key.depthWriteEnable = false;
key.depthCompareOp = VK_COMPARE_OP_ALWAYS;
}
// PSP color/alpha mask is per bit but we can only support per byte.
// But let's do that, at least. And let's try a threshold.
bool rmask = (gstate.pmskc & 0xFF) < 128;
bool gmask = ((gstate.pmskc >> 8) & 0xFF) < 128;
bool bmask = ((gstate.pmskc >> 16) & 0xFF) < 128;
bool amask = (gstate.pmska & 0xFF) < 128;
u8 abits = (gstate.pmska >> 0) & 0xFF;
#ifndef MOBILE_DEVICE
u8 rbits = (gstate.pmskc >> 0) & 0xFF;
u8 gbits = (gstate.pmskc >> 8) & 0xFF;
u8 bbits = (gstate.pmskc >> 16) & 0xFF;
if ((rbits != 0 && rbits != 0xFF) || (gbits != 0 && gbits != 0xFF) || (bbits != 0 && bbits != 0xFF)) {
WARN_LOG_REPORT_ONCE(rgbmask, G3D, "Unsupported RGB mask: r=%02x g=%02x b=%02x", rbits, gbits, bbits);
}
if (abits != 0 && abits != 0xFF) {
// The stencil part of the mask is supported.
WARN_LOG_REPORT_ONCE(amask, G3D, "Unsupported alpha/stencil mask: %02x", abits);
}
#endif
// Let's not write to alpha if stencil isn't enabled.
if (!gstate.isStencilTestEnabled()) {
amask = false;
} else {
// If the stencil type is set to KEEP, we shouldn't write to the stencil/alpha channel.
if (ReplaceAlphaWithStencilType() == STENCIL_VALUE_KEEP) {
amask = false;
}
}
key.colorWriteMask = (rmask ? VK_COLOR_COMPONENT_R_BIT : 0) | (gmask ? VK_COLOR_COMPONENT_G_BIT : 0) | (bmask ? VK_COLOR_COMPONENT_B_BIT : 0) | (amask ? VK_COLOR_COMPONENT_A_BIT : 0);
// Stencil Test
if (gstate.isStencilTestEnabled()) {
key.stencilTestEnable = true;
key.stencilCompareOp = compareOps[gstate.getStencilTestFunction()];
dynState.stencilRef = gstate.getStencilTestRef();
dynState.stencilCompareMask = gstate.getStencilTestMask();
key.stencilFailOp = stencilOps[gstate.getStencilOpSFail()]; // stencil fail
key.stencilDepthFailOp = stencilOps[gstate.getStencilOpZFail()]; // depth fail
key.stencilPassOp = stencilOps[gstate.getStencilOpZPass()]; // depth pass
dynState.stencilWriteMask = ~abits;
} else {
key.stencilTestEnable = false;
}
}
key.topology = primToVulkan[prim];
VkViewport &vp = dynState.viewport;
vp.x = vpAndScissor.viewportX;
vp.y = vpAndScissor.viewportY;
vp.width = vpAndScissor.viewportW;
vp.height = vpAndScissor.viewportH;
vp.minDepth = vpAndScissor.depthRangeMin;
vp.maxDepth = vpAndScissor.depthRangeMax;
if (vpAndScissor.dirtyProj) {
shaderManager->DirtyUniform(DIRTY_PROJMATRIX);
}
VkRect2D &scissor = dynState.scissor;
scissor.offset.x = vpAndScissor.scissorX;
scissor.offset.y = vpAndScissor.scissorY;
scissor.extent.width = vpAndScissor.scissorW;
scissor.extent.height = vpAndScissor.scissorH;
float depthMin = vpAndScissor.depthRangeMin;
float depthMax = vpAndScissor.depthRangeMax;
if (depthMin < 0.0f) depthMin = 0.0f;
if (depthMax > 1.0f) depthMax = 1.0f;
if (vpAndScissor.dirtyDepth) {
shaderManager->DirtyUniform(DIRTY_DEPTHRANGE);
}
}
//void DrawEngineVulkan::ApplyDrawStateLate() {
/*
// At this point, we know if the vertices are full alpha or not.
// TODO: Set the nearest/linear here (since we correctly know if alpha/color tests are needed)?
if (!gstate.isModeClear()) {
// TODO: Test texture?
textureCache_->ApplyTexture();
if (fboTexNeedBind_) {
// Note that this is positions, not UVs, that we need the copy from.
framebufferManager_->BindFramebufferColor(1, nullptr, BINDFBCOLOR_MAY_COPY);
// If we are rendering at a higher resolution, linear is probably best for the dest color.
pD3Ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
pD3Ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
fboTexBound_ = true;
fboTexNeedBind_ = false;
}
}
*/
//}

View File

@ -0,0 +1,63 @@
#pragma once
#include "Common/Vulkan/VulkanLoader.h"
#include <cstring>
class FramebufferManagerVulkan;
struct VulkanDynamicState {
VkViewport viewport;
VkRect2D scissor;
bool useBlendColor;
uint32_t blendColor;
bool useStencil;
uint8_t stencilRef;
uint8_t stencilWriteMask;
uint8_t stencilCompareMask;
};
// Let's pack this tight using bitfields.
// If an enable flag is set to 0, all the data fields for that section should
// also be set to 0.
// ~54 bits.
// Can't use enums unfortunately, they end up signed and breaking values above half their ranges.
struct VulkanPipelineRasterStateKey {
// Blend
unsigned int blendEnable : 1;
unsigned int srcColor : 5; // VkBlendFactor
unsigned int destColor : 5; // VkBlendFactor
unsigned int srcAlpha : 5; // VkBlendFactor
unsigned int destAlpha : 5; // VkBlendFactor
// bool useBlendConstant : 1; // sacrifice a bit to cheaply check if we need to update the blend color
unsigned int blendOpColor : 3; // VkBlendOp
unsigned int blendOpAlpha : 3; // VkBlendOp
unsigned int logicOpEnable : 1;
unsigned int logicOp : 4; // VkLogicOp
unsigned int colorWriteMask : 4;
// Depth/Stencil
unsigned int depthTestEnable : 1;
unsigned int depthWriteEnable : 1;
unsigned int depthCompareOp : 3; // VkCompareOp
unsigned int stencilTestEnable : 1;
unsigned int stencilCompareOp : 3; // VkCompareOp
unsigned int stencilPassOp : 4; // VkStencilOp
unsigned int stencilFailOp : 4; // VkStencilOp
unsigned int stencilDepthFailOp : 4; // VkStencilOp
// We'll use dynamic state for writemask, reference and comparemask to start with,
// and viewport/scissor.
// Rasterizer
unsigned int cullMode : 2; // VkCullModeFlagBits
unsigned int topology : 4; // VkPrimitiveTopology
bool operator < (const VulkanPipelineRasterStateKey &other) const {
size_t size = sizeof(VulkanPipelineRasterStateKey);
return memcmp(this, &other, size) < 0;
}
};
class ShaderManagerVulkan;
void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,184 @@
// Copyright (c) 2015- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include <map>
#include "Globals.h"
#include "GPU/GPUInterface.h"
#include "GPU/GPUState.h"
#include "Common/Vulkan/VulkanContext.h"
#include "GPU/Vulkan/TextureScalerVulkan.h"
#include "GPU/Common/TextureCacheCommon.h"
struct VirtualFramebuffer;
class FramebufferManagerVulkan;
class DepalShaderCacheVulkan;
class ShaderManagerVulkan;
class DrawEngineVulkan;
class VulkanContext;
class VulkanTexture;
class VulkanPushBuffer;
struct SamplerCacheKey {
SamplerCacheKey() : fullKey(0) {}
union {
u32 fullKey;
struct {
bool mipEnable : 1;
bool minFilt : 1;
bool mipFilt : 1;
bool magFilt : 1;
bool sClamp : 1;
bool tClamp : 1;
int lodBias : 4;
int maxLevel : 4;
};
};
bool operator < (const SamplerCacheKey &other) const {
return fullKey < other.fullKey;
}
};
class CachedTextureVulkan {
public:
CachedTextureVulkan() : texture_(nullptr) {
}
~CachedTextureVulkan();
// TODO: Switch away from VulkanImage to some kind of smart suballocating texture pool.
VulkanTexture *texture_;
};
class SamplerCache {
public:
SamplerCache(VulkanContext *vulkan) : vulkan_(vulkan) {}
~SamplerCache();
VkSampler GetOrCreateSampler(const SamplerCacheKey &key);
private:
VulkanContext *vulkan_;
std::map<SamplerCacheKey, VkSampler> cache_;
};
class TextureCacheVulkan : public TextureCacheCommon {
public:
TextureCacheVulkan(VulkanContext *vulkan);
~TextureCacheVulkan();
void SetTexture(VulkanPushBuffer *uploadBuffer);
virtual bool SetOffsetTexture(u32 offset) override;
void Clear(bool delete_them);
void StartFrame();
void Invalidate(u32 addr, int size, GPUInvalidationType type);
void InvalidateAll(GPUInvalidationType type);
void ClearNextFrame();
void SetFramebufferManager(FramebufferManagerVulkan *fbManager) {
framebufferManager_ = fbManager;
}
void SetDepalShaderCache(DepalShaderCacheVulkan *dpCache) {
depalShaderCache_ = dpCache;
}
void SetShaderManager(ShaderManagerVulkan *sm) {
shaderManager_ = sm;
}
void SetTransformDrawEngine(DrawEngineVulkan *td) {
transformDraw_ = td;
}
size_t NumLoadedTextures() const {
return cache.size();
}
void ForgetLastTexture() {
lastBoundTexture = nullptr;
gstate_c.textureChanged |= TEXCHANGE_PARAMSONLY;
}
void ApplyTexture(VkImageView &imageView, VkSampler &sampler);
protected:
void DownloadFramebufferForClut(u32 clutAddr, u32 bytes);
private:
void Decimate(); // Run this once per frame to get rid of old textures.
void DeleteTexture(TexCache::iterator it);
void *ReadIndexedTex(int level, const u8 *texptr, int bytesPerIndex, VkFormat dstFmt, int bufw);
void UpdateSamplingParams(TexCacheEntry &entry, SamplerCacheKey &key);
void LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePtr, int rowPitch, int level, bool replaceImages, int scaleFactor, VkFormat dstFmt);
VkFormat GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const;
void *DecodeTextureLevel(GETextureFormat format, GEPaletteFormat clutformat, int level, u32 &texByteAlign, VkFormat dstFmt, int scaleFactor, int *bufw = 0);
TexCacheEntry::Status CheckAlpha(const u32 *pixelData, VkFormat dstFmt, int stride, int w, int h);
template <typename T>
const T *GetCurrentClut();
u32 GetCurrentClutHash();
void UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutBase, bool clutIndexIsSimple);
bool AttachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, u32 texaddrOffset = 0) override;
void DetachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer) override;
void SetTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer);
void ApplyTextureFramebuffer(VkCommandBuffer cmd, TexCacheEntry *entry, VirtualFramebuffer *framebuffer, VkImageView &image, VkSampler &sampler);
void SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight, SamplerCacheKey &key);
VulkanContext *vulkan_;
TexCache secondCache;
std::vector<u32> nameCache_;
u32 cacheSizeEstimate_;
u32 secondCacheSizeEstimate_;
// Separate to keep main texture cache size down.
struct AttachedFramebufferInfo {
u32 xOffset;
u32 yOffset;
};
std::map<u32, AttachedFramebufferInfo> fbTexInfo_;
void AttachFramebufferValid(TexCacheEntry *entry, VirtualFramebuffer *framebuffer, const AttachedFramebufferInfo &fbInfo);
void AttachFramebufferInvalid(TexCacheEntry *entry, VirtualFramebuffer *framebuffer, const AttachedFramebufferInfo &fbInfo);
bool clearCacheNextFrame_;
bool lowMemoryMode_;
SamplerCache samplerCache_;
TextureScalerVulkan scaler;
u32 *clutBuf_;
u32 clutHash_;
// True if the clut is just alpha values in the same order (RGBA4444-bit only.)
bool clutAlphaLinear_;
u16 clutAlphaLinearColor_;
CachedTextureVulkan *lastBoundTexture;
int decimationCounter_;
int texelsScaledThisFrame_;
int timesInvalidatedAllThisFrame_;
FramebufferManagerVulkan *framebufferManager_;
DepalShaderCacheVulkan *depalShaderCache_;
ShaderManagerVulkan *shaderManager_;
DrawEngineVulkan *transformDraw_;
};
VkFormat getClutDestFormatVulkan(GEPaletteFormat format);

View File

@ -0,0 +1,62 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#if _MSC_VER == 1700
// Has to be included before TextureScaler.h, else we get those std::bind errors in VS2012..
#include "../native/base/basictypes.h"
#endif
#include <algorithm>
#include "gfx/gl_common.h"
#include "GPU/Common/TextureScalerCommon.h"
#include "GPU/Vulkan/TextureScalerVulkan.h"
#include "Common/ColorConv.h"
#include "Common/Log.h"
#include "Common/ThreadPools.h"
int TextureScalerVulkan::BytesPerPixel(u32 format) {
return (format == GL_UNSIGNED_BYTE) ? 4 : 2;
}
u32 TextureScalerVulkan::Get8888Format() {
return GL_UNSIGNED_BYTE;
}
void TextureScalerVulkan::ConvertTo8888(u32 format, u32* source, u32* &dest, int width, int height) {
switch (format) {
case GL_UNSIGNED_BYTE:
dest = source; // already fine
break;
case GL_UNSIGNED_SHORT_4_4_4_4:
GlobalThreadPool::Loop(std::bind(&convert4444_gl, (u16*)source, dest, width, placeholder::_1, placeholder::_2), 0, height);
break;
case GL_UNSIGNED_SHORT_5_6_5:
GlobalThreadPool::Loop(std::bind(&convert565_gl, (u16*)source, dest, width, placeholder::_1, placeholder::_2), 0, height);
break;
case GL_UNSIGNED_SHORT_5_5_5_1:
GlobalThreadPool::Loop(std::bind(&convert5551_gl, (u16*)source, dest, width, placeholder::_1, placeholder::_2), 0, height);
break;
default:
dest = source;
ERROR_LOG(G3D, "iXBRZTexScaling: unsupported texture format");
}
}

View File

@ -0,0 +1,27 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include "Common/CommonTypes.h"
#include "GPU/Common/TextureScalerCommon.h"
class TextureScalerVulkan : public TextureScaler {
void ConvertTo8888(u32 format, u32* source, u32* &dest, int width, int height) override;
int BytesPerPixel(u32 format) override;
u32 Get8888Format() override;
};

View File

@ -0,0 +1,512 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <cstdio>
#include <cstdlib>
#include <locale.h>
#include "gfx_es2/gpu_features.h"
#if defined(_WIN32) && defined(_DEBUG)
#include "Common/CommonWindows.h"
#endif
#include "base/stringutil.h"
#include "Common/Vulkan/VulkanLoader.h"
#include "GPU/ge_constants.h"
#include "GPU/GPUState.h"
#include "GPU/Common/ShaderId.h"
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Vulkan/VertexShaderGeneratorVulkan.h"
#include "GPU/Vulkan/PipelineManagerVulkan.h"
#include "GPU/Vulkan/ShaderManagerVulkan.h"
static const char *vulkan_glsl_preamble =
"#version 400\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"#extension GL_ARB_shading_language_420pack : enable\n\n";
// "Varying" layout - must match fragment shader
// color0 = 0
// color1 = 1
// texcoord = 2
// fog = 3
// SDL 1.2 on Apple does not have support for OpenGL 3 and hence needs
// special treatment in the shader generator.
#ifdef __APPLE__
#define FORCE_OPENGL_2_0
#endif
#undef WRITE
#define WRITE p+=sprintf
static const char * const boneWeightDecl[9] = {
"#ERROR#",
"layout(location = 3) in float w1;\n",
"layout(location = 3) in vec2 w1;\n",
"layout(location = 3) in vec3 w1;\n",
"layout(location = 3) in vec4 w1;\n",
"layout(location = 3) in vec4 w1;\nlayout(location = 4) in float w2;\n",
"layout(location = 3) in vec4 w1;\nlayout(location = 4) in vec2 w2;\n",
"layout(location = 3) in vec4 w1;\nlayout(location = 4) in vec3 w2;\n",
"layout(location = 3) in vec4 w1;\nlayout(location = 4) in vec4 w2;\n",
};
enum DoLightComputation {
LIGHT_OFF,
LIGHT_SHADE,
LIGHT_FULL,
};
// Depth range and viewport
//
// After the multiplication with the projection matrix, we have a 4D vector in clip space.
// In OpenGL, Z is from -1 to 1, while in D3D, Z is from 0 to 1.
// PSP appears to use the OpenGL convention. As Z is from -1 to 1, and the viewport is represented
// by a center and a scale, to find the final Z value, all we need to do is to multiply by ZScale and
// add ZCenter - these are properly scaled to directly give a Z value in [0, 65535].
//
// z = vec.z * ViewportZScale + ViewportZCenter;
//
// That will give us the final value between 0 and 65535, which we can simply floor to simulate
// the limited precision of the PSP's depth buffer. Then we convert it back:
// z = floor(z);
//
// vec.z = (z - ViewportZCenter) / ViewportZScale;
//
// Now, the regular machinery will take over and do the calculation again.
//
// All this above is for full transform mode.
// In through mode, the Z coordinate just goes straight through and there is no perspective division.
// We simulate this of course with pretty much an identity matrix. Rounding Z becomes very easy.
//
// TODO: Skip all this if we can actually get a 16-bit depth buffer along with stencil, which
// is a bit of a rare configuration, although quite common on mobile.
bool GenerateVulkanGLSLVertexShader(const ShaderID &id, char *buffer, bool *usesLighting) {
char *p = buffer;
// #define USE_FOR_LOOP
WRITE(p, "%s", vulkan_glsl_preamble);
bool highpFog = false;
bool highpTexcoord = false;
bool isModeThrough = id.Bit(VS_BIT_IS_THROUGH);
bool lmode = id.Bit(VS_BIT_LMODE) && !isModeThrough; // TODO: Different expression than in shaderIDgen
bool doTexture = id.Bit(VS_BIT_DO_TEXTURE);
bool doTextureProjection = id.Bit(VS_BIT_DO_TEXTURE_PROJ);
GETexMapMode uvGenMode = static_cast<GETexMapMode>(id.Bits(VS_BIT_UVGEN_MODE, 2));
// this is only valid for some settings of uvGenMode
GETexProjMapMode uvProjMode = static_cast<GETexProjMapMode>(id.Bits(VS_BIT_UVPROJ_MODE, 2));
bool doShadeMapping = uvGenMode == GE_TEXMAP_ENVIRONMENT_MAP;
bool doFlatShading = id.Bit(VS_BIT_FLATSHADE);
bool useHWTransform = id.Bit(VS_BIT_USE_HW_TRANSFORM);
bool hasColor = id.Bit(VS_BIT_HAS_COLOR) || !useHWTransform;
bool hasNormal = id.Bit(VS_BIT_HAS_NORMAL) && useHWTransform;
bool hasTexcoord = id.Bit(VS_BIT_HAS_TEXCOORD) || !useHWTransform;
bool enableFog = id.Bit(VS_BIT_ENABLE_FOG);
bool throughmode = id.Bit(VS_BIT_IS_THROUGH);
bool flipNormal = id.Bit(VS_BIT_NORM_REVERSE);
int ls0 = id.Bits(VS_BIT_LS0, 2);
int ls1 = id.Bits(VS_BIT_LS1, 2);
bool enableBones = id.Bit(VS_BIT_ENABLE_BONES);
bool enableLighting = id.Bit(VS_BIT_LIGHTING_ENABLE);
int matUpdate = id.Bits(VS_BIT_MATERIAL_UPDATE, 3);
// The uniforms are passed in as three "clumps" that may or may not be present.
// We will memcpy the parts into place in a big buffer so we can be quite dynamic about what parts
// are present and what parts aren't, but we will not be ultra detailed about it.
*usesLighting = enableLighting || doShadeMapping;
WRITE(p, "\n");
WRITE(p, "layout (std140, set = 0, binding = 2) uniform baseVars {\n%s} base;\n", ub_baseStr);
if (enableLighting || doShadeMapping)
WRITE(p, "layout (std140, set = 0, binding = 3) uniform lightVars {\n%s} light;\n", ub_vs_lightsStr);
if (enableBones)
WRITE(p, "layout (std140, set = 0, binding = 4) uniform boneVars {\n%s} bone;\n", ub_vs_bonesStr);
const char *shading = doFlatShading ? "flat " : "";
DoLightComputation doLight[4] = { LIGHT_OFF, LIGHT_OFF, LIGHT_OFF, LIGHT_OFF };
if (useHWTransform) {
int shadeLight0 = doShadeMapping ? ls0 : -1;
int shadeLight1 = doShadeMapping ? ls1 : -1;
for (int i = 0; i < 4; i++) {
if (i == shadeLight0 || i == shadeLight1)
doLight[i] = LIGHT_SHADE;
if (id.Bit(VS_BIT_LIGHTING_ENABLE) && id.Bit(VS_BIT_LIGHT0_ENABLE + i))
doLight[i] = LIGHT_FULL;
}
}
int numBoneWeights = 0;
int boneWeightScale = id.Bits(VS_BIT_WEIGHT_FMTSCALE, 2);
if (enableBones) {
numBoneWeights = 1 + id.Bits(VS_BIT_BONES, 3);
WRITE(p, "%s", boneWeightDecl[numBoneWeights]);
}
int texFmtScale = id.Bits(VS_BIT_TEXCOORD_FMTSCALE, 2);
if (useHWTransform)
WRITE(p, "layout (location = %d) in vec3 position;\n", PspAttributeLocation::POSITION);
else
// we pass the fog coord in w
WRITE(p, "layout (location = %d) in vec4 position;\n", PspAttributeLocation::POSITION);
if (useHWTransform && hasNormal)
WRITE(p, "layout (location = %d) in vec3 normal;\n", PspAttributeLocation::NORMAL);
if (doTexture && hasTexcoord) {
if (!useHWTransform && doTextureProjection && !throughmode)
WRITE(p, "layout (location = %d) in vec3 texcoord;\n", PspAttributeLocation::TEXCOORD);
else
WRITE(p, "layout (location = %d) in vec2 texcoord;\n", PspAttributeLocation::TEXCOORD);
}
if (hasColor) {
WRITE(p, "layout (location = %d) in vec4 color0;\n", PspAttributeLocation::COLOR0);
if (lmode && !useHWTransform) // only software transform supplies color1 as vertex data
WRITE(p, "layout (location = %d) in vec3 color1;\n", PspAttributeLocation::COLOR1);
}
bool prescale = false;
WRITE(p, "layout (location = 1) %sout vec4 v_color0;\n", shading);
if (lmode) {
WRITE(p, "layout (location = 2) %sout vec3 v_color1;\n", shading);
}
if (doTexture) {
if (doTextureProjection) {
WRITE(p, "layout (location = 0) out vec3 v_texcoord;\n");
} else {
WRITE(p, "layout (location = 0) out vec2 v_texcoord;\n");
}
}
if (enableFog) {
// See the fragment shader generator
WRITE(p, "layout (location = 3) out float v_fogdepth;\n");
}
// See comment above this function (GenerateVertexShader).
if (!isModeThrough && gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) {
// Apply the projection and viewport to get the Z buffer value, floor to integer, undo the viewport and projection.
WRITE(p, "\nvec4 depthRoundZVP(vec4 v) {\n");
WRITE(p, " float z = v.z / v.w;\n");
WRITE(p, " z = z * base.depthRange.x + base.depthRange.y;\n");
WRITE(p, " z = floor(z);\n");
WRITE(p, " z = (z - base.depthRange.z) * base.depthRange.w;\n");
WRITE(p, " return vec4(v.x, v.y, z * v.w, v.w);\n");
WRITE(p, "}\n\n");
}
WRITE(p, "void main() {\n");
if (!useHWTransform) {
// Simple pass-through of vertex data to fragment shader
if (doTexture) {
if (throughmode && doTextureProjection) {
WRITE(p, " v_texcoord = vec3(texcoord, 1.0);\n");
} else {
WRITE(p, " v_texcoord = texcoord;\n");
}
}
if (hasColor) {
WRITE(p, " v_color0 = color0;\n");
if (lmode)
WRITE(p, " v_color1 = color1;\n");
} else {
WRITE(p, " v_color0 = base.matambientalpha;\n");
if (lmode)
WRITE(p, " v_color1 = vec3(0.0);\n");
}
if (enableFog) {
WRITE(p, " v_fogdepth = position.w;\n");
}
if (isModeThrough) {
WRITE(p, " gl_Position = base.proj_through_mtx * vec4(position.xyz, 1.0);\n");
} else {
// The viewport is used in this case, so need to compensate for that.
if (gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) {
WRITE(p, " gl_Position = depthRoundZVP(base.proj_mtx * vec4(position.xyz, 1.0));\n");
} else {
WRITE(p, " gl_Position = base.proj_mtx * vec4(position.xyz, 1.0);\n");
}
}
} else {
// Step 1: World Transform / Skinning
if (!enableBones) {
// No skinning, just standard T&L.
WRITE(p, " vec3 worldpos = (base.world_mtx * vec4(position.xyz, 1.0)).xyz;\n");
if (hasNormal)
WRITE(p, " mediump vec3 worldnormal = normalize((base.world_mtx * vec4(%snormal, 0.0)).xyz);\n", flipNormal ? "-" : "");
else
WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n");
} else {
static const char *rescale[4] = { "", " * 1.9921875", " * 1.999969482421875", "" }; // 2*127.5f/128.f, 2*32767.5f/32768.f, 1.0f};
const char *factor = rescale[boneWeightScale];
static const char * const boneWeightAttr[8] = {
"w1.x", "w1.y", "w1.z", "w1.w",
"w2.x", "w2.y", "w2.z", "w2.w",
};
WRITE(p, " mat4 skinMatrix = w1.x * bone.m[0];\n");
if (numBoneWeights > 1) {
for (int i = 1; i < numBoneWeights; i++) {
WRITE(p, " skinMatrix += %s * bone.m[%i];\n", boneWeightAttr[i], i);
}
}
WRITE(p, ";\n");
// Trying to simplify this results in bugs in LBP...
WRITE(p, " vec3 skinnedpos = (skinMatrix * vec4(position, 1.0)).xyz %s;\n", factor);
WRITE(p, " vec3 worldpos = (base.world_mtx * vec4(skinnedpos, 1.0)).xyz;\n");
if (hasNormal) {
WRITE(p, " mediump vec3 skinnednormal = (skinMatrix * vec4(%snormal, 0.0)).xyz %s;\n", flipNormal ? "-" : "", factor);
} else {
WRITE(p, " mediump vec3 skinnednormal = (skinMatrix * vec4(0.0, 0.0, %s1.0, 0.0)).xyz %s;\n", flipNormal ? "-" : "", factor);
}
WRITE(p, " mediump vec3 worldnormal = normalize((base.world_mtx * vec4(skinnednormal, 0.0)).xyz);\n");
}
WRITE(p, " vec4 viewPos = base.view_mtx * vec4(worldpos, 1.0);\n");
// Final view and projection transforms.
if (gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) {
WRITE(p, " gl_Position = depthRoundZVP(base.proj_mtx * viewPos);\n");
} else {
WRITE(p, " gl_Position = base.proj_mtx * viewPos;\n");
}
// TODO: Declare variables for dots for shade mapping if needed.
const char *ambientStr = ((matUpdate & 1) && hasColor) ? "color0" : "base.matambientalpha";
const char *diffuseStr = ((matUpdate & 2) && hasColor) ? "color0.rgb" : "light.matdiffuse";
const char *specularStr = ((matUpdate & 4) && hasColor) ? "color0.rgb" : "light.matspecular.rgb";
bool diffuseIsZero = true;
bool specularIsZero = true;
bool distanceNeeded = false;
if (enableLighting) {
WRITE(p, " vec4 lightSum0 = light.globalAmbient * %s + vec4(light.matemissive, 0.0);\n", ambientStr);
for (int i = 0; i < 4; i++) {
GELightType type = static_cast<GELightType>(id.Bits(VS_BIT_LIGHT0_TYPE + 4 * i, 2));
GELightComputation comp = static_cast<GELightComputation>(id.Bits(VS_BIT_LIGHT0_COMP + 4 * i, 2));
if (doLight[i] != LIGHT_FULL)
continue;
diffuseIsZero = false;
if (comp != GE_LIGHTCOMP_ONLYDIFFUSE)
specularIsZero = false;
if (type != GE_LIGHTTYPE_DIRECTIONAL)
distanceNeeded = true;
}
if (!specularIsZero) {
WRITE(p, " vec3 lightSum1 = vec3(0.0);\n");
}
if (!diffuseIsZero) {
WRITE(p, " vec3 toLight;\n");
WRITE(p, " vec3 diffuse;\n");
}
if (distanceNeeded) {
WRITE(p, " float distance;\n");
WRITE(p, " float lightScale;\n");
}
}
// Calculate lights if needed. If shade mapping is enabled, lights may need to be
// at least partially calculated.
for (int i = 0; i < 4; i++) {
if (doLight[i] != LIGHT_FULL)
continue;
GELightType type = static_cast<GELightType>(id.Bits(VS_BIT_LIGHT0_TYPE + 4 * i, 2));
GELightComputation comp = static_cast<GELightComputation>(id.Bits(VS_BIT_LIGHT0_COMP + 4 * i, 2));
if (type == GE_LIGHTTYPE_DIRECTIONAL) {
// We prenormalize light positions for directional lights.
WRITE(p, " toLight = light.pos[%i];\n", i);
} else {
WRITE(p, " toLight = light.pos[%i] - worldpos;\n", i);
WRITE(p, " distance = length(toLight);\n");
WRITE(p, " toLight /= distance;\n");
}
bool doSpecular = comp != GE_LIGHTCOMP_ONLYDIFFUSE;
bool poweredDiffuse = comp == GE_LIGHTCOMP_BOTHWITHPOWDIFFUSE;
WRITE(p, " mediump float dot%i = max(dot(toLight, worldnormal), 0.0);\n", i);
if (poweredDiffuse) {
// pow(0.0, 0.0) may be undefined, but the PSP seems to treat it as 1.0.
// Seen in Tales of the World: Radiant Mythology (#2424.)
WRITE(p, " if (dot%i == 0.0 && light.matspecular.a == 0.0) {\n", i);
WRITE(p, " dot%i = 1.0;\n", i);
WRITE(p, " } else {\n");
WRITE(p, " dot%i = pow(dot%i, light.matspecular.a);\n", i, i);
WRITE(p, " }\n");
}
const char *timesLightScale = " * lightScale";
// Attenuation
switch (type) {
case GE_LIGHTTYPE_DIRECTIONAL:
timesLightScale = "";
break;
case GE_LIGHTTYPE_POINT:
WRITE(p, " lightScale = clamp(1.0 / dot(light.att[%i], vec3(1.0, distance, distance*distance)), 0.0, 1.0);\n", i);
break;
case GE_LIGHTTYPE_SPOT:
case GE_LIGHTTYPE_UNKNOWN:
WRITE(p, " float angle%i = dot(normalize(light.dir[%i]), toLight);\n", i, i);
WRITE(p, " if (angle[%i] >= light.angle[%i]) {\n", i, i);
WRITE(p, " lightScale = clamp(1.0 / dot(light.att[%i], vec3(1.0, distance, distance*distance)), 0.0, 1.0) * pow(angle[%i], light.spotCoef[%i]);\n", i, i, i);
WRITE(p, " } else {\n");
WRITE(p, " lightScale = 0.0;\n");
WRITE(p, " }\n");
break;
default:
// ILLEGAL
break;
}
WRITE(p, " diffuse = (light.diffuse[%i] * %s) * dot%i;\n", i, diffuseStr, i);
if (doSpecular) {
WRITE(p, " dot%i = dot(normalize(toLight + vec3(0.0, 0.0, 1.0)), worldnormal);\n", i);
WRITE(p, " if (dot%i > 0.0)\n", i);
WRITE(p, " lightSum1 += light.specular[%i] * %s * (pow(dot%i, light.matspecular.a) %s);\n", i, specularStr, i, timesLightScale);
}
WRITE(p, " lightSum0.rgb += (light.ambient[%i] * %s.rgb + diffuse)%s;\n", i, ambientStr, timesLightScale);
}
if (enableLighting) {
// Sum up ambient, emissive here.
if (lmode) {
WRITE(p, " v_color0 = clamp(lightSum0, 0.0, 1.0);\n");
// v_color1 only exists when lmode = 1.
if (specularIsZero) {
WRITE(p, " v_color1 = vec3(0.0);\n");
} else {
WRITE(p, " v_color1 = clamp(lightSum1, 0.0, 1.0);\n");
}
} else {
if (specularIsZero) {
WRITE(p, " v_color0 = clamp(lightSum0, 0.0, 1.0);\n");
} else {
WRITE(p, " v_color0 = clamp(clamp(lightSum0, 0.0, 1.0) + vec4(lightSum1, 0.0), 0.0, 1.0);\n");
}
}
} else {
// Lighting doesn't affect color.
if (hasColor) {
WRITE(p, " v_color0 = color0;\n");
} else {
WRITE(p, " v_color0 = base.matambientalpha;\n");
}
if (lmode) {
WRITE(p, " v_color1 = vec3(0.0);\n");
}
}
// Step 3: UV generation
if (doTexture) {
switch (uvGenMode) {
case GE_TEXMAP_TEXTURE_COORDS: // Scale-offset. Easy.
case GE_TEXMAP_UNKNOWN: // Not sure what this is, but Riviera uses it. Treating as coords works.
if (prescale) {
if (hasTexcoord) {
WRITE(p, " v_texcoord = texcoord;\n");
} else {
WRITE(p, " v_texcoord = vec2(0.0);\n");
}
} else {
if (hasTexcoord) {
WRITE(p, " v_texcoord = texcoord * base.uvscaleoffset.xy + base.uvscaleoffset.zw;\n");
} else {
WRITE(p, " v_texcoord = base.uvscaleoffset.zw;\n");
}
}
break;
case GE_TEXMAP_TEXTURE_MATRIX: // Projection mapping.
{
std::string temp_tc;
switch (uvProjMode) {
case GE_PROJMAP_POSITION: // Use model space XYZ as source
temp_tc = "vec4(position.xyz, 1.0)";
break;
case GE_PROJMAP_UV: // Use unscaled UV as source
{
// prescale is false here.
if (hasTexcoord) {
static const char *rescaleuv[4] = { "", " * 1.9921875", " * 1.999969482421875", "" }; // 2*127.5f/128.f, 2*32767.5f/32768.f, 1.0f};
const char *factor = rescaleuv[texFmtScale];
temp_tc = StringFromFormat("vec4(texcoord.xy %s, 0.0, 1.0)", factor);
} else {
temp_tc = "vec4(0.0, 0.0, 0.0, 1.0)";
}
}
break;
case GE_PROJMAP_NORMALIZED_NORMAL: // Use normalized transformed normal as source
if (hasNormal)
temp_tc = flipNormal ? "vec4(normalize(-normal), 1.0)" : "vec4(normalize(normal), 1.0)";
else
temp_tc = "vec4(0.0, 0.0, 1.0, 1.0)";
break;
case GE_PROJMAP_NORMAL: // Use non-normalized transformed normal as source
if (hasNormal)
temp_tc = flipNormal ? "vec4(-normal, 1.0)" : "vec4(normal, 1.0)";
else
temp_tc = "vec4(0.0, 0.0, 1.0, 1.0)";
break;
}
// Transform by texture matrix. XYZ as we are doing projection mapping.
WRITE(p, " v_texcoord = (base.tex_mtx * %s).xyz * vec3(base.uvscaleoffset.xy, 1.0);\n", temp_tc.c_str());
}
break;
case GE_TEXMAP_ENVIRONMENT_MAP: // Shade mapping - use dots from light sources.
WRITE(p, " v_texcoord = base.uvscaleoffset.xy * vec2(1.0 + dot(normalize(light.pos[%i]), worldnormal), 1.0 + dot(normalize(light.pos[%i]), worldnormal)) * 0.5;\n", ls0, ls1);
break;
default:
// ILLEGAL
break;
}
}
// Compute fogdepth
if (enableFog)
WRITE(p, " v_fogdepth = (viewPos.z + base.fogcoef_stencilreplace.x) * base.fogcoef_stencilreplace.y;\n");
}
WRITE(p, "}\n");
return true;
}

View File

@ -0,0 +1,5 @@
#pragma once
#include "GPU/Common/ShaderId.h"
bool GenerateVulkanGLSLVertexShader(const ShaderID &id, char *buffer, bool *usesLighting);

45
GPU/Vulkan/VulkanUtil.cpp Normal file
View File

@ -0,0 +1,45 @@
// Copyright (c) 2016- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "GPU/Vulkan/VulkanUtil.h"
VulkanFBO::VulkanFBO() : color_(nullptr), depthStencil_(nullptr) {}
VulkanFBO::~VulkanFBO() {
delete color_;
delete depthStencil_;
}
void VulkanFBO::Create(VulkanContext *vulkan, VkRenderPass rp_compatible, int width, int height, VkFormat color_Format) {
color_ = new VulkanTexture(vulkan);
VkImageCreateFlags flags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
color_->CreateDirect(width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, flags | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
depthStencil_->CreateDirect(width, height, 1, VK_FORMAT_D24_UNORM_S8_UINT, flags | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
VkImageView views[2] = { color_->GetImageView(), depthStencil_->GetImageView() };
VkFramebufferCreateInfo fb = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
fb.pAttachments = views;
fb.attachmentCount = 2;
fb.flags = 0;
fb.renderPass = rp_compatible;
fb.width = width;
fb.height = height;
fb.layers = 1;
vkCreateFramebuffer(vulkan->GetDevice(), &fb, nullptr, &framebuffer_);
}

48
GPU/Vulkan/VulkanUtil.h Normal file
View File

@ -0,0 +1,48 @@
// Copyright (c) 2016- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include "Common/Vulkan/VulkanLoader.h"
#include "Common/Vulkan/VulkanImage.h"
// Vulkan doesn't really have the concept of an FBO that owns the images,
// but it does have the concept of a framebuffer as a set of attachments.
// VulkanFBO is an approximation of the FBO concept the other backends use
// to make things as similar as possible without being suboptimal.
//
class VulkanFBO {
public:
VulkanFBO();
~VulkanFBO();
// Depth-format is chosen automatically depending on hardware support.
// Color format will be 32-bit RGBA.
void Create(VulkanContext *vulkan, VkRenderPass rp_compatible, int width, int height, VkFormat colorFormat);
VulkanTexture *GetColor() { return color_; }
VulkanTexture *GetDepthStencil() { return depthStencil_; }
VkFramebuffer GetFramebuffer() { return framebuffer_; }
private:
VulkanTexture *color_;
VulkanTexture *depthStencil_;
// This point specifically to color and depth.
VkFramebuffer framebuffer_;
};

View File

@ -72,4 +72,5 @@ HEADERS += $$P/Common/ChunkFile.h \
$$P/Common/Crypto/*.h
INCLUDEPATH += $$P/ext/native
INCLUDEPATH += $$P/ext

View File

@ -1,5 +1,5 @@
VERSION = 1.2.2.0
DEFINES += USING_QT_UI USE_FFMPEG
DEFINES += USING_QT_UI USE_FFMPEG NO_VULKAN
exists( /usr/include/snappy-c.h ) {
DEFINES += SHARED_SNAPPY

View File

@ -466,6 +466,14 @@ void SystemInfoScreen::CreateViews() {
eglExtensions->Add(new TextView(exts[i]))->SetFocusable(true);
}
}
} else if (g_Config.iGPUBackend == GPU_BACKEND_VULKAN) {
tabHolder->AddTab("Vulkan Features", oglExtensionsScroll);
oglExtensions->Add(new ItemHeader("Vulkan Features"));
std::vector<std::string> features = thin3d->GetFeatureList();
for (auto &feature : features) {
oglExtensions->Add(new TextView(feature))->SetFocusable(true);
}
}
}

View File

@ -66,6 +66,10 @@
#include "UI/InstallZipScreen.h"
#include "UI/ProfilerDraw.h"
#ifdef _WIN32
#include "Windows/MainWindow.h"
#endif
EmuScreen::EmuScreen(const std::string &filename)
: bootPending_(true), gamePath_(filename), invalid_(true), quit_(false), pauseTrigger_(false), saveStatePreviewShownTime_(0.0), saveStatePreview_(nullptr) {
memset(axisState_, 0, sizeof(axisState_));
@ -99,14 +103,36 @@ void EmuScreen::bootGame(const std::string &filename) {
CoreParameter coreParam;
coreParam.cpuCore = g_Config.bJit ? CPU_JIT : CPU_INTERPRETER;
coreParam.gpuCore = GPU_GLES;
if (GetGPUBackend() == GPUBackend::DIRECT3D9) {
switch (GetGPUBackend()) {
case GPUBackend::OPENGL:
coreParam.gpuCore = GPU_GLES;
break;
case GPUBackend::DIRECT3D9:
coreParam.gpuCore = GPU_DIRECTX9;
break;
case GPUBackend::DIRECT3D11:
coreParam.gpuCore = GPU_DIRECTX11;
break;
case GPUBackend::VULKAN:
coreParam.gpuCore = GPU_VULKAN;
if (g_Config.iRenderingMode != FB_NON_BUFFERED_MODE) {
#ifdef _WIN32
if (IDYES == MessageBox(MainWindow::GetHWND(), L"The Vulkan backend is not yet compatible with buffered rendering. Switch to non-buffered (WARNING: This will cause glitches with the other backends unless you switch back)", L"Vulkan Experimental Support", MB_ICONINFORMATION | MB_YESNO)) {
g_Config.iRenderingMode = FB_NON_BUFFERED_MODE;
} else {
errorMessage_ = "Non-buffered rendering required for Vulkan";
return;
}
#endif
}
break;
}
if (g_Config.bSoftwareRendering) {
coreParam.gpuCore = GPU_SOFTWARE;
}
// Preserve the existing graphics context.
coreParam.graphicsContext = PSP_CoreParameter().graphicsContext;
coreParam.thin3d = screenManager()->getThin3DContext();
coreParam.enableSound = g_Config.bEnableSound;
coreParam.fileToStart = filename;
coreParam.mountIso = "";
@ -691,7 +717,7 @@ void EmuScreen::update(InputState &input) {
UIScreen::update(input);
// Simply forcibily update to the current screen size every frame. Doesn't cost much.
// Simply forcibly update to the current screen size every frame. Doesn't cost much.
// If bounds is set to be smaller than the actual pixel resolution of the display, respect that.
// TODO: Should be able to use g_dpi_scale here instead. Might want to store the dpi scale in the UI context too.
const Bounds &bounds = screenManager()->getUIContext()->GetBounds();
@ -897,6 +923,7 @@ void EmuScreen::render() {
while (coreState == CORE_RUNNING) {
PSP_RunLoopFor(blockTicks);
}
// Hopefully coreState is now CORE_NEXTFRAME
if (coreState == CORE_NEXTFRAME) {
// set back to running for the next frame
@ -956,6 +983,7 @@ void EmuScreen::render() {
}
// We have no use for backbuffer depth or stencil, so let tiled renderers discard them after tiling.
/*
if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) {
GLenum attachments[2] = { GL_DEPTH, GL_STENCIL };
glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments);
@ -969,6 +997,7 @@ void EmuScreen::render() {
}
#endif
}
*/
}
void EmuScreen::deviceLost() {

View File

@ -135,10 +135,18 @@ void GameSettingsScreen::CreateViews() {
tabHolder->AddTab(ms->T("Graphics"), graphicsSettingsScroll);
graphicsSettings->Add(new ItemHeader(gr->T("Rendering Mode")));
#if defined(_WIN32)
static const char *renderingBackend[] = { "OpenGL", "Direct3D9" };
static const char *renderingBackend[] = { "OpenGL", "Direct3D 9", "Direct3D 11", "Vulkan (experimental)" };
PopupMultiChoice *renderingBackendChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iGPUBackend, gr->T("Backend"), renderingBackend, GPU_BACKEND_OPENGL, ARRAY_SIZE(renderingBackend), gr->GetName(), screenManager()));
renderingBackendChoice->OnChoice.Handle(this, &GameSettingsScreen::OnRenderingBackend);
#if !defined(_WIN32)
renderingBackendChoice->HideChoice(1); // D3D9
renderingBackendChoice->HideChoice(2); // D3D11
#else
renderingBackendChoice->HideChoice(2); // D3D11
#endif
#if !defined(ANDROID) && !defined(_WIN32)
// TODO: Add dynamic runtime check for Vulkan support on Android
renderingBackendChoice->HideChoice(3);
#endif
static const char *renderingMode[] = { "Non-Buffered Rendering", "Buffered Rendering", "Read Framebuffers To Memory (CPU)", "Read Framebuffers To Memory (GPU)"};
PopupMultiChoice *renderingModeChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iRenderingMode, gr->T("Mode"), renderingMode, 0, ARRAY_SIZE(renderingMode), gr->GetName(), screenManager()));

View File

@ -698,13 +698,20 @@ void NativeRender(GraphicsContext *graphicsContext) {
// Apply the UIContext bounds as a 2D transformation matrix.
Matrix4x4 ortho;
if (GetGPUBackend() == GPUBackend::DIRECT3D9) {
switch (GetGPUBackend()) {
case GPUBackend::VULKAN:
ortho.setOrthoD3D(0.0f, xres, 0, yres, -1.0f, 1.0f);
break;
case GPUBackend::DIRECT3D9:
case GPUBackend::DIRECT3D11:
ortho.setOrthoD3D(0.0f, xres, yres, 0.0f, -1.0f, 1.0f);
Matrix4x4 translation;
translation.setTranslation(Vec3(-0.5f, -0.5f, 0.0f));
ortho = translation * ortho;
} else {
break;
case GPUBackend::OPENGL:
ortho.setOrtho(0.0f, xres, yres, 0.0f, -1.0f, 1.0f);
break;
}
ui_draw2d.SetDrawMatrix(ortho);
@ -715,7 +722,9 @@ void NativeRender(GraphicsContext *graphicsContext) {
screenManager->getUIContext()->Text()->OncePerFrame();
}
DrawDownloadsOverlay(*screenManager->getUIContext());
// At this point, the vulkan context has been "ended" already, no more drawing can be done in this frame.
// TODO: Integrate the download overlay with the screen system
// DrawDownloadsOverlay(*screenManager->getUIContext());
if (g_TakeScreenshot) {
TakeScreenshot();

View File

@ -104,7 +104,6 @@
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140_xp</PlatformToolset>
<WholeProgramOptimization>false</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
@ -212,6 +211,7 @@
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StringPooling>true</StringPooling>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -227,4 +227,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -128,6 +128,7 @@ unsigned int WINAPI TheThread(void *)
I18NCategory *err = GetI18NCategory("Error");
Reporting::ReportMessage("Graphics init error: %s", error_string.c_str());
const char *defaultErrorVulkan = "Failed initializing graphics. Try upgrading your graphics drivers.\n\nWould you like to try switching to OpenGL?\n\nError message:";
const char *defaultErrorOpenGL = "Failed initializing graphics. Try upgrading your graphics drivers.\n\nWould you like to try switching to DirectX 9?\n\nError message:";
const char *defaultErrorDirect3D9 = "Failed initializing graphics. Try upgrading your graphics drivers and directx 9 runtime.\n\nWould you like to try switching to OpenGL?\n\nError message:";
const char *genericError;
@ -137,6 +138,10 @@ unsigned int WINAPI TheThread(void *)
nextBackend = GPU_BACKEND_OPENGL;
genericError = err->T("GenericDirect3D9Error", defaultErrorDirect3D9);
break;
case GPU_BACKEND_VULKAN:
nextBackend = GPU_BACKEND_OPENGL;
genericError = err->T("GenericVulkanError", defaultErrorDirect3D9);
break;
case GPU_BACKEND_OPENGL:
default:
nextBackend = GPU_BACKEND_DIRECT3D9;

View File

@ -0,0 +1,223 @@
// Copyright (c) 2015- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
// Initializing a Vulkan context is quite a complex task!
// That's not really a strange thing though - you really do have control over everything,
// and everything needs to be specified. There are no nebulous defaults.
// We create a swapchain, and two framebuffers that we can point to two of the images
// we got from the swap chain. These will be used as backbuffers.
//
// We also create a depth buffer. The swap chain will not allocate one for us so we need
// to manage the memory for it ourselves.
// The depth buffer will not really be used unless we do "non-buffered" rendering, which will happen
// directly to one of the backbuffers.
//
// Render pass usage
//
// In normal buffered rendering mode, we do not begin the "UI" render pass until after we have rendered
// a frame of PSP graphics. The render pass that we will use then will be the simple "uiPass" that does not
// bother attaching the depth buffer, and discards all input (no need to even bother clearing as we will
// draw over the whole backbuffer anyway).
//
// However, in non-buffered, we will have to use the depth buffer, and we must begin the rendering pass
// before we start rendering PSP graphics, and end it only after we have completed rendering the UI on top.
// We will also use clearing.
//
// So it all turns into a single rendering pass, which might be good for performance on some GPUs, but it
// will complicate things a little.
//
// In a first iteration, we will not distinguish between these two cases - we will always create a depth buffer
// and use the same render pass configuration (clear to black). However, we can later change this so we switch
// to a non-clearing render pass in buffered mode, which might be a tiny bit faster.
#include <assert.h>
#include <crtdbg.h>
#include <sstream>
#include "Common/Vulkan/VulkanLoader.h"
#include "Common/Vulkan/VulkanContext.h"
#include "thin3d/thin3d.h"
#include "util/text/parsers.h"
#include "Windows/GPU/WindowsVulkanContext.h"
extern const char *PPSSPP_GIT_VERSION;
#ifdef _DEBUG
static const bool g_validate_ = true;
#else
static const bool g_validate_ = false;
#endif
static VulkanContext *g_Vulkan;
struct VulkanLogOptions {
bool breakOnWarning;
bool breakOnError;
bool msgBoxOnError;
};
static VulkanLogOptions g_LogOptions;
const char *ObjTypeToString(VkDebugReportObjectTypeEXT type) {
switch (type) {
case VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT: return "Instance";
case VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT: return "PhysicalDevice";
case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT: return "Device";
case VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT: return "Queue";
case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT: return "CommandBuffer";
case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT: return "DeviceMemory";
case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT: return "Buffer";
case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT: return "BufferView";
case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT: return "Image";
case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT: return "ImageView";
case VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT: return "ShaderModule";
case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT: return "Pipeline";
case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT: return "PipelineLayout";
case VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT: return "Sampler";
case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT: return "DescriptorSet";
case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT: return "DescriptorSetLayout";
case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT: return "DescriptorPool";
case VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT: return "Fence";
case VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT: return "Semaphore";
case VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT: return "Event";
case VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT: return "QueryPool";
case VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT: return "Framebuffer";
case VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT: return "RenderPass";
case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT: return "PipelineCache";
case VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT: return "SurfaceKHR";
case VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT: return "SwapChainKHR";
case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT: return "CommandPool";
default: return "";
}
}
static VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, void *pUserData) {
const VulkanLogOptions *options = (const VulkanLogOptions *)pUserData;
std::ostringstream message;
if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
message << "ERROR: ";
} else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
message << "WARNING: ";
} else if (msgFlags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) {
message << "PERFORMANCE WARNING: ";
} else if (msgFlags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
message << "INFO: ";
} else if (msgFlags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
message << "DEBUG: ";
}
message << "[" << pLayerPrefix << "] " << ObjTypeToString(objType) << " Code " << msgCode << " : " << pMsg << "\n";
// Getting some bizarre false positives for mapping image memory.
// https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/issues/121
if (msgCode == 6 && (!memcmp(pMsg, "Cannot map", 10) || !memcmp(pMsg, "Cannot sub", 10)))
return false;
#ifdef _WIN32
OutputDebugStringA(message.str().c_str());
if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
if (options->breakOnError) {
DebugBreak();
}
if (options->msgBoxOnError) {
MessageBoxA(NULL, message.str().c_str(), "Alert", MB_OK);
}
} else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
if (options->breakOnWarning) {
DebugBreak();
}
}
#else
std::cout << message;
#endif
// false indicates that layer should not bail-out of an
// API call that had validation failures. This may mean that the
// app dies inside the driver due to invalid parameter(s).
// That's what would happen without validation layers, so we'll
// keep that behavior here.
return false;
}
bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_message) {
*error_message = "N/A";
if (g_Vulkan) {
*error_message = "Already initialized";
return false;
}
init_glslang();
g_LogOptions.breakOnError = true;
g_LogOptions.breakOnWarning = true;
g_LogOptions.msgBoxOnError = false;
Version gitVer(PPSSPP_GIT_VERSION);
g_Vulkan = new VulkanContext("PPSSPP", gitVer.ToInteger(), (g_validate_ ? VULKAN_FLAG_VALIDATE : 0) | VULKAN_FLAG_PRESENT_MAILBOX);
if (g_Vulkan->CreateDevice(0) != VK_SUCCESS) {
*error_message = g_Vulkan->InitError();
return false;
}
if (g_validate_) {
int bits = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
g_Vulkan->InitDebugMsgCallback(Vulkan_Dbg, bits, &g_LogOptions);
}
g_Vulkan->InitSurfaceWin32(hInst, hWnd);
g_Vulkan->InitObjects(true);
_CrtCheckMemory();
return true;
}
void WindowsVulkanContext::Shutdown() {
g_Vulkan->WaitUntilQueueIdle();
g_Vulkan->DestroyObjects();
g_Vulkan->DestroyDevice();
g_Vulkan->DestroyDebugMsgCallback();
delete g_Vulkan;
g_Vulkan = nullptr;
finalize_glslang();
}
Thin3DContext *WindowsVulkanContext::CreateThin3DContext() {
return T3DCreateVulkanContext(g_Vulkan);
}
void WindowsVulkanContext::SwapBuffers() {
}
void WindowsVulkanContext::Resize() {
/*
g_Vulkan->DestroyObjects();
g_Vulkan->WaitUntilQueueIdle();
g_Vulkan->InitObjects(g_Vulkan)
*/
}
void WindowsVulkanContext::SwapInterval(int interval) {
}
void *WindowsVulkanContext::GetAPIContext() {
return g_Vulkan;
}

View File

@ -0,0 +1,36 @@
// Copyright (c) 2015- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include "Common/GraphicsContext.h"
#include "Windows/GPU/WindowsGraphicsContext.h"
#include "thin3d/thin3d.h"
class WindowsVulkanContext : public WindowsGraphicsContext {
public:
bool Init(HINSTANCE hInst, HWND window, std::string *error_message) override;
void Shutdown() override;
void SwapInterval(int interval) override;
void SwapBuffers() override;
void Resize() override;
void *GetAPIContext();
Thin3DContext *CreateThin3DContext() override;
};

View File

@ -701,6 +701,12 @@ namespace MainWindow {
PostMessage(MainWindow::GetHWND(), WM_CLOSE, 0, 0);
break;
case ID_OPTIONS_VULKAN:
g_Config.iGPUBackend = GPU_BACKEND_VULKAN;
g_Config.bRestartRequired = true;
PostMessage(MainWindow::GetHWND(), WM_CLOSE, 0, 0);
break;
case ID_OPTIONS_NONBUFFEREDRENDERING: setRenderingMode(FB_NON_BUFFERED_MODE); break;
case ID_OPTIONS_BUFFEREDRENDERING: setRenderingMode(FB_BUFFERED_MODE); break;
case ID_OPTIONS_READFBOTOMEMORYCPU: setRenderingMode(FB_READFBOMEMORY_CPU); break;
@ -1144,14 +1150,31 @@ namespace MainWindow {
CheckMenuItem(menu, savestateSlot[i], MF_BYCOMMAND | ((i == g_Config.iCurrentStateSlot) ? MF_CHECKED : MF_UNCHECKED));
}
if (g_Config.iGPUBackend == GPU_BACKEND_DIRECT3D9) {
switch (g_Config.iGPUBackend) {
case GPU_BACKEND_DIRECT3D9:
EnableMenuItem(menu, ID_OPTIONS_DIRECT3D9, MF_GRAYED);
CheckMenuItem(menu, ID_OPTIONS_DIRECT3D9, MF_CHECKED);
EnableMenuItem(menu, ID_OPTIONS_OPENGL, MF_ENABLED);
} else {
EnableMenuItem(menu, ID_OPTIONS_OPENGL, MF_GRAYED);
CheckMenuItem(menu, ID_OPTIONS_OPENGL, MF_CHECKED);
EnableMenuItem(menu, ID_OPTIONS_VULKAN, MF_ENABLED);
CheckMenuItem(menu, ID_OPTIONS_DIRECT3D9, MF_CHECKED);
CheckMenuItem(menu, ID_OPTIONS_OPENGL, MF_UNCHECKED);
CheckMenuItem(menu, ID_OPTIONS_VULKAN, MF_UNCHECKED);
break;
case GPU_BACKEND_OPENGL:
EnableMenuItem(menu, ID_OPTIONS_DIRECT3D9, MF_ENABLED);
EnableMenuItem(menu, ID_OPTIONS_OPENGL, MF_GRAYED);
EnableMenuItem(menu, ID_OPTIONS_VULKAN, MF_ENABLED);
CheckMenuItem(menu, ID_OPTIONS_DIRECT3D9, MF_UNCHECKED);
CheckMenuItem(menu, ID_OPTIONS_OPENGL, MF_CHECKED);
CheckMenuItem(menu, ID_OPTIONS_VULKAN, MF_UNCHECKED);
break;
case GPU_BACKEND_VULKAN:
EnableMenuItem(menu, ID_OPTIONS_DIRECT3D9, MF_ENABLED);
EnableMenuItem(menu, ID_OPTIONS_OPENGL, MF_ENABLED);
EnableMenuItem(menu, ID_OPTIONS_VULKAN, MF_GRAYED);
CheckMenuItem(menu, ID_OPTIONS_DIRECT3D9, MF_UNCHECKED);
CheckMenuItem(menu, ID_OPTIONS_OPENGL, MF_UNCHECKED);
CheckMenuItem(menu, ID_OPTIONS_VULKAN, MF_CHECKED);
break;
}
UpdateDynamicMenuCheckmarks(menu);

View File

@ -38,6 +38,7 @@ EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "native", "..\ext\native\native.vcxproj", "{C4DF647E-80EA-4111-A0A8-218B1B711E18}"
ProjectSection(ProjectDependencies) = postProject
{F761046E-6C38-4428-A5F1-38391A37BB34} = {F761046E-6C38-4428-A5F1-38391A37BB34}
{EDFA2E87-8AC1-4853-95D4-D7594FF81947} = {EDFA2E87-8AC1-4853-95D4-D7594FF81947}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PPSSPPHeadless", "..\headless\Headless.vcxproj", "{EE9BD869-CAA3-447D-8328-294D90DE2C1F}"

View File

@ -39,7 +39,6 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>false</WholeProgramOptimization>
<PlatformToolset>v140_xp</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
@ -125,7 +124,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>../common;..;../ext/native;../ext/native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../ext;../common;..;../ext/native;../ext/native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
<ForcedIncludeFiles>stdafx.h;Common/DbgNew.h</ForcedIncludeFiles>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
@ -135,7 +134,7 @@
<Optimization>Disabled</Optimization>
</ClCompile>
<Link>
<AdditionalDependencies>Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;dxerr.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<TargetMachine>MachineX86</TargetMachine>
@ -161,7 +160,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>../common;..;../ext/native;../ext/native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../ext;../common;..;../ext/native;../ext/native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
<ForcedIncludeFiles>stdafx.h;Common/DbgNew.h</ForcedIncludeFiles>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<OmitFramePointers>false</OmitFramePointers>
@ -175,11 +174,10 @@
<AdditionalDependencies>Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>$(OutDir)$(ProjectName).pdb</ProgramDatabaseFile>
<SubSystem>Windows</SubSystem>
<TargetMachine>MachineX64</TargetMachine>
<LargeAddressAware>true</LargeAddressAware>
<ShowProgress>NotSet</ShowProgress>
<AdditionalOptions>/ignore:4049 /ignore:4217 %(AdditionalOptions)</AdditionalOptions>
<SubSystem>Windows</SubSystem>
</Link>
<PreBuildEvent>
<Command>../Windows/git-version-gen.cmd</Command>
@ -201,14 +199,14 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>../common;..;../ext/native;../ext/native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../ext;../common;..;../ext/native;../ext/native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
<ForcedIncludeFiles>stdafx.h</ForcedIncludeFiles>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
</ClCompile>
<Link>
<AdditionalDependencies>dxerr.lib;Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -246,13 +244,13 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>../common;..;../ext/native;../ext/native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>../ext;../common;..;../ext/native;../ext/native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
<ForcedIncludeFiles>stdafx.h</ForcedIncludeFiles>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
</ClCompile>
<Link>
<AdditionalDependencies>dxerr.lib;Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
@ -260,6 +258,7 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<TargetMachine>MachineX64</TargetMachine>
<AdditionalOptions>/ignore:4049 /ignore:4217 %(AdditionalOptions)</AdditionalOptions>
<LargeAddressAware>true</LargeAddressAware>
</Link>
<PreBuildEvent>
<Command>../Windows/git-version-gen.cmd</Command>
@ -319,6 +318,7 @@
<ClCompile Include="MainWindowMenu.cpp" />
<ClCompile Include="RawInput.cpp" />
<ClCompile Include="TouchInputHandler.cpp" />
<ClCompile Include="GPU\WindowsVulkanContext.cpp" />
<ClCompile Include="W32Util\DialogManager.cpp" />
<ClCompile Include="W32Util\Misc.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)%(Filename)2.obj</ObjectFileName>
@ -374,6 +374,7 @@
<ClInclude Include="MainWindowMenu.h" />
<ClInclude Include="RawInput.h" />
<ClInclude Include="TouchInputHandler.h" />
<ClInclude Include="GPU\WindowsVulkanContext.h" />
<ClInclude Include="W32Util\DialogManager.h" />
<ClInclude Include="W32Util\Misc.h" />
<ClInclude Include="W32Util\PropertySheet.h" />
@ -422,6 +423,9 @@
<ProjectReference Include="..\Core\Core.vcxproj">
<Project>{533f1d30-d04d-47cc-ad71-20f658907e36}</Project>
</ProjectReference>
<ProjectReference Include="..\ext\glslang.vcxproj">
<Project>{edfa2e87-8ac1-4853-95d4-d7594ff81947}</Project>
</ProjectReference>
<ProjectReference Include="..\ext\libkirk\libkirk.vcxproj">
<Project>{3baae095-e0ab-4b0e-b5df-ce39c8ae31de}</Project>
</ProjectReference>
@ -449,4 +453,4 @@
<UserProperties RESOURCE_FILE="DaSh.rc" />
</VisualStudio>
</ProjectExtensions>
</Project>
</Project>

View File

@ -152,6 +152,7 @@
<ClCompile Include="GPU\WindowsGLContext.cpp">
<Filter>Windows\System</Filter>
</ClCompile>
<ClCompile Include="GPU\WindowsVulkanContext.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Debugger\CtrlDisAsmView.h">
@ -278,6 +279,7 @@
<ClInclude Include="GPU\WindowsGraphicsContext.h">
<Filter>Windows\System</Filter>
</ClInclude>
<ClInclude Include="GPU\WindowsVulkanContext.h" />
</ItemGroup>
<ItemGroup>
<None Include="icon1.ico">

View File

@ -43,6 +43,7 @@
#include "Windows/WindowsHost.h"
#include "Windows/MainWindow.h"
#include "Windows/GPU/WindowsGLContext.h"
#include "Windows/GPU/WindowsVulkanContext.h"
#include "Windows/GPU/D3D9Context.h"
#include "Windows/Debugger/DebuggerShared.h"
@ -68,7 +69,9 @@ static BOOL PostDialogMessage(Dialog *dialog, UINT message, WPARAM wParam = 0, L
}
WindowsHost::WindowsHost(HINSTANCE hInstance, HWND mainWindow, HWND displayWindow)
: gfx_(nullptr), hInstance_(hInstance), mainWindow_(mainWindow), displayWindow_(displayWindow)
: gfx_(nullptr), hInstance_(hInstance),
mainWindow_(mainWindow),
displayWindow_(displayWindow)
{
mouseDeltaX = 0;
mouseDeltaY = 0;
@ -110,6 +113,9 @@ bool WindowsHost::InitGraphics(std::string *error_message, GraphicsContext **ctx
case GPU_BACKEND_DIRECT3D9:
graphicsContext = new D3D9Context();
break;
case GPU_BACKEND_VULKAN:
graphicsContext = new WindowsVulkanContext();
break;
default:
return false;
}

View File

@ -481,6 +481,7 @@ BEGIN
BEGIN
MENUITEM "Direct3D9" ID_OPTIONS_DIRECT3D9
MENUITEM "OpenGL" ID_OPTIONS_OPENGL
MENUITEM "Vulkan" ID_OPTIONS_VULKAN
END
POPUP "Rendering Mode"

View File

@ -323,6 +323,7 @@
#define ID_EMULATION_ROTATION_H_R 40158
#define ID_EMULATION_ROTATION_V_R 40159
#define ID_OPTIONS_DISPLAY_LAYOUT 40160
#define ID_OPTIONS_VULKAN 40161
// Dummy option to let the buffered rendering hotkey cycle through all the options.
#define ID_OPTIONS_BUFFEREDRENDERINGDUMMY 40500

View File

@ -124,9 +124,29 @@ EGL_FILES := \
$(SRC)/Common/GL/GLInterface/EGLAndroid.cpp \
$(SRC)/Common/GL/GLInterface/GLInterface.cpp
VULKAN_FILES := \
$(SRC)/Common/Vulkan/VulkanLoader.cpp \
$(SRC)/Common/Vulkan/VulkanContext.cpp \
$(SRC)/Common/Vulkan/VulkanImage.cpp \
$(SRC)/Common/Vulkan/VulkanMemory.cpp \
$(SRC)/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp \
$(SRC)/GPU/Vulkan/DrawEngineVulkan.cpp \
$(SRC)/GPU/Vulkan/FramebufferVulkan.cpp \
$(SRC)/GPU/Vulkan/GPU_Vulkan.cpp \
$(SRC)/GPU/Vulkan/PipelineManagerVulkan.cpp \
$(SRC)/GPU/Vulkan/ShaderManagerVulkan.cpp \
$(SRC)/GPU/Vulkan/StateMappingVulkan.cpp \
$(SRC)/GPU/Vulkan/TextureCacheVulkan.cpp \
$(SRC)/GPU/Vulkan/TextureScalerVulkan.cpp \
$(SRC)/GPU/Vulkan/DepalettizeShaderVulkan.cpp \
$(SRC)/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp \
$(SRC)/GPU/Vulkan/VulkanUtil.cpp
#endif
EXEC_AND_LIB_FILES := \
$(ARCH_FILES) \
$(EGL_FILES) \
$(VULKAN_FILES) \
TestRunner.cpp \
$(SRC)/Core/MIPS/MIPS.cpp.arm \
$(SRC)/Core/MIPS/MIPSAnalyst.cpp \
@ -492,6 +512,7 @@ endif
$(call import-module,libzip)
$(call import-module,native)
$(call import-module,glslang)
ifeq ($(ANDROID_NDK_PROFILER),1)
$(call import-module,android-ndk-profiler)

View File

@ -8,13 +8,14 @@ LOCAL_CPPFLAGS := -fno-exceptions -std=gnu++11 -fno-rtti -Wno-reorder -Wno-liter
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/../../Common \
$(LOCAL_PATH)/../.. \
$(LOCAL_PATH)/../../ext \
$(LOCAL_PATH)/$(NATIVE)/base \
$(LOCAL_PATH)/$(NATIVE)/ext \
$(LOCAL_PATH)/$(NATIVE)/ext/libzip \
$(LOCAL_PATH)/$(NATIVE) \
$(LOCAL_PATH)
LOCAL_STATIC_LIBRARIES := native libzip
LOCAL_STATIC_LIBRARIES := native libzip glslang
LOCAL_LDLIBS := -lz -landroid -lGLESv2 -lOpenSLES -lEGL -ldl -llog
# ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)

View File

@ -4,6 +4,9 @@
// It calls a set of methods defined in NativeApp.h. These should be implemented
// by your game or app.
#include <assert.h>
#include <sstream>
#include <jni.h>
#include <android/log.h>
#include <android/native_window_jni.h>
@ -30,8 +33,12 @@
#include "gfx/gl_common.h"
#include "gfx_es2/gpu_features.h"
#include "thin3d/thin3d.h"
#include "Core/Config.cpp"
#include "Common/GraphicsContext.h"
#include "Common/GL/GLInterfaceBase.h"
#include "Common/Vulkan/VulkanLoader.h"
#include "Common/Vulkan/VulkanContext.h"
#include "UI/GameInfoCache.h"
#include "app-android.h"
@ -56,7 +63,12 @@ struct FrameCommand {
std::string params;
};
class AndroidEGLGraphicsContext : public GraphicsContext {
class AndroidGraphicsContext : public GraphicsContext {
public:
virtual bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) = 0;
};
class AndroidEGLGraphicsContext : public AndroidGraphicsContext {
public:
AndroidEGLGraphicsContext() : wnd_(nullptr), gl(nullptr) {}
bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion);
@ -137,6 +149,157 @@ public:
};
static const bool g_validate_ = true;
static VulkanContext *g_Vulkan;
class AndroidVulkanContext : public AndroidGraphicsContext {
public:
AndroidVulkanContext() {}
bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) override;
void Shutdown() override;
void SwapInterval(int interval) override;
void SwapBuffers() override;
void Resize() override;
void *GetAPIContext() { return g_Vulkan; }
Thin3DContext *CreateThin3DContext() override {
return T3DCreateVulkanContext(g_Vulkan);
}
};
struct VulkanLogOptions {
bool breakOnWarning;
bool breakOnError;
bool msgBoxOnError;
};
static VulkanLogOptions g_LogOptions;
const char *ObjTypeToString(VkDebugReportObjectTypeEXT type) {
switch (type) {
case VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT: return "Instance";
case VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT: return "PhysicalDevice";
case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT: return "Device";
case VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT: return "Queue";
case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT: return "CommandBuffer";
case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT: return "DeviceMemory";
case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT: return "Buffer";
case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT: return "BufferView";
case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT: return "Image";
case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT: return "ImageView";
case VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT: return "ShaderModule";
case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT: return "Pipeline";
case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT: return "PipelineLayout";
case VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT: return "Sampler";
case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT: return "DescriptorSet";
case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT: return "DescriptorSetLayout";
case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT: return "DescriptorPool";
case VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT: return "Fence";
case VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT: return "Semaphore";
case VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT: return "Event";
case VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT: return "QueryPool";
case VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT: return "Framebuffer";
case VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT: return "RenderPass";
case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT: return "PipelineCache";
case VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT: return "SurfaceKHR";
case VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT: return "SwapChainKHR";
case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT: return "CommandPool";
default: return "Unknown";
}
}
static VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, void *pUserData) {
const VulkanLogOptions *options = (const VulkanLogOptions *)pUserData;
int loglevel = ANDROID_LOG_INFO;
if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
loglevel = ANDROID_LOG_ERROR;
} else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
loglevel = ANDROID_LOG_WARN;
} else if (msgFlags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) {
loglevel = ANDROID_LOG_WARN;
} else if (msgFlags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
loglevel = ANDROID_LOG_WARN;
} else if (msgFlags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
loglevel = ANDROID_LOG_WARN;
}
__android_log_print(loglevel, APP_NAME, "[%s] %s Code %d : %s", pLayerPrefix, ObjTypeToString(objType), msgCode, pMsg);
// false indicates that layer should not bail-out of an
// API call that had validation failures. This may mean that the
// app dies inside the driver due to invalid parameter(s).
// That's what would happen without validation layers, so we'll
// keep that behavior here.
return false;
}
bool AndroidVulkanContext::Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) {
if (g_Vulkan) {
return false;
}
init_glslang();
g_LogOptions.breakOnError = true;
g_LogOptions.breakOnWarning = true;
g_LogOptions.msgBoxOnError = false;
ILOG("Creating vulkan context");
Version gitVer(PPSSPP_GIT_VERSION);
g_Vulkan = new VulkanContext("PPSSPP", gitVer.ToInteger(), VULKAN_FLAG_PRESENT_MAILBOX | VULKAN_FLAG_PRESENT_FIFO_RELAXED);
if (!g_Vulkan->GetInstance()) {
ELOG("Failed to create vulkan context");
return false;
}
ILOG("Creating vulkan device");
if (g_Vulkan->CreateDevice(0) != VK_SUCCESS) {
return false;
}
int width = desiredBackbufferSizeX;
int height = desiredBackbufferSizeY;
if (!width || !height) {
width = pixel_xres;
height = pixel_yres;
}
ILOG("InitSurfaceAndroid: width=%d height=%d", width, height);
g_Vulkan->InitSurfaceAndroid(wnd, width, height);
if (g_validate_) {
int bits = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
g_Vulkan->InitDebugMsgCallback(Vulkan_Dbg, bits, &g_LogOptions);
}
g_Vulkan->InitObjects(true);
return true;
}
void AndroidVulkanContext::Shutdown() {
g_Vulkan->WaitUntilQueueIdle();
g_Vulkan->DestroyObjects();
g_Vulkan->DestroyDebugMsgCallback();
g_Vulkan->DestroyDevice();
delete g_Vulkan;
g_Vulkan = nullptr;
finalize_glslang();
}
void AndroidVulkanContext::SwapBuffers() {
}
void AndroidVulkanContext::Resize() {
/*
g_Vulkan->DestroyObjects();
g_Vulkan->WaitUntilQueueIdle();
g_Vulkan->InitObjects(g_Vulkan)
*/
}
void AndroidVulkanContext::SwapInterval(int interval) {
}
static recursive_mutex frameCommandLock;
static std::queue<FrameCommand> frameCommands;
@ -827,8 +990,16 @@ extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(J
ELOG("Error: Surface is null.");
return false;
}
bool vulkan = g_Config.iGPUBackend == GPU_BACKEND_VULKAN;
AndroidGraphicsContext *graphicsContext;
if (vulkan) {
graphicsContext = new AndroidVulkanContext();
} else {
graphicsContext = new AndroidEGLGraphicsContext();
}
AndroidEGLGraphicsContext *graphicsContext = new AndroidEGLGraphicsContext();
if (!graphicsContext->Init(wnd, desiredBackbufferSizeX, desiredBackbufferSizeY, backbuffer_format, androidVersion)) {
ELOG("Failed to initialize graphics context.");
delete graphicsContext;
@ -883,7 +1054,7 @@ extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(J
renderer_inited = false;
graphicsContext->Shutdown();
delete graphicsContext;
renderLoopRunning = false;
WLOG("Render loop function exited.");
return true;

View File

@ -114,6 +114,8 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>glslang/glslang/OSDependent/Windows;glslang/glslang/MachineIndependent</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -132,6 +134,8 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>glslang/glslang/OSDependent/Windows;glslang/glslang/MachineIndependent</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -140,13 +144,11 @@
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<Text Include="glslang\glslang\ReadMe.txt" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="glslang\glslang\GenericCodeGen\CodeGen.cpp" />
<ClCompile Include="glslang\glslang\GenericCodeGen\Link.cpp" />
<ClCompile Include="glslang\glslang\MachineIndependent\Constant.cpp" />
<ClCompile Include="glslang\glslang\MachineIndependent\glslang_tab.cpp" />
<ClCompile Include="glslang\glslang\MachineIndependent\InfoSink.cpp" />
<ClCompile Include="glslang\glslang\MachineIndependent\Initialize.cpp" />
<ClCompile Include="glslang\glslang\MachineIndependent\Intermediate.cpp" />
@ -191,6 +193,7 @@
<ClInclude Include="glslang\glslang\Include\ResourceLimits.h" />
<ClInclude Include="glslang\glslang\Include\ShHandle.h" />
<ClInclude Include="glslang\glslang\Include\Types.h" />
<ClInclude Include="glslang\glslang\MachineIndependent\glslang_tab.cpp.h" />
<ClInclude Include="glslang\glslang\MachineIndependent\gl_types.h" />
<ClInclude Include="glslang\glslang\MachineIndependent\Initialize.h" />
<ClInclude Include="glslang\glslang\MachineIndependent\localintermediate.h" />
@ -202,7 +205,6 @@
<ClInclude Include="glslang\glslang\MachineIndependent\Scan.h" />
<ClInclude Include="glslang\glslang\MachineIndependent\ScanContext.h" />
<ClInclude Include="glslang\glslang\MachineIndependent\SymbolTable.h" />
<ClInclude Include="glslang\glslang\MachineIndependent\unistd.h" />
<ClInclude Include="glslang\glslang\MachineIndependent\Versions.h" />
<ClInclude Include="glslang\glslang\Public\ShaderLang.h" />
<ClInclude Include="glslang\OGLCompilersDLL\InitializeDll.h" />
@ -217,9 +219,8 @@
</ItemGroup>
<ItemGroup>
<None Include="glslang\glslang\MachineIndependent\glslang.y" />
<None Include="glslang\glslang\MachineIndependent\Makefile" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -1,8 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Text Include="glslang\glslang\ReadMe.txt" />
</ItemGroup>
<ItemGroup>
<Filter Include="SPIRV">
<UniqueIdentifier>{300f8b24-cec5-41dc-bf7f-e60745f3e383}</UniqueIdentifier>
@ -110,6 +107,9 @@
<ClCompile Include="glslang\SPIRV\InReadableOrder.cpp">
<Filter>SPIRV</Filter>
</ClCompile>
<ClCompile Include="glslang\glslang\MachineIndependent\glslang_tab.cpp">
<Filter>glslang</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="glslang\SPIRV\disassemble.h">
@ -202,9 +202,6 @@
<ClInclude Include="glslang\glslang\MachineIndependent\SymbolTable.h">
<Filter>glslang</Filter>
</ClInclude>
<ClInclude Include="glslang\glslang\MachineIndependent\unistd.h">
<Filter>glslang</Filter>
</ClInclude>
<ClInclude Include="glslang\glslang\MachineIndependent\Versions.h">
<Filter>glslang</Filter>
</ClInclude>
@ -212,13 +209,13 @@
<Filter>glslang</Filter>
</ClInclude>
<ClInclude Include="glslang\OGLCompilersDLL\InitializeDll.h" />
<ClInclude Include="glslang\glslang\MachineIndependent\glslang_tab.cpp.h">
<Filter>glslang</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="glslang\glslang\MachineIndependent\glslang.y">
<Filter>glslang</Filter>
</None>
<None Include="glslang\glslang\MachineIndependent\Makefile">
<Filter>glslang</Filter>
</None>
</ItemGroup>
</Project>

View File

@ -67,7 +67,6 @@
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140_xp</PlatformToolset>
<WholeProgramOptimization>false</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
@ -221,6 +220,8 @@
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalIncludeDirectories>armips</AdditionalIncludeDirectories>
<BufferSecurityCheck>false</BufferSecurityCheck>
<StringPooling>true</StringPooling>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -333,4 +334,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -46,7 +46,6 @@
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140_xp</PlatformToolset>
<WholeProgramOptimization>false</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
@ -129,6 +128,7 @@
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StringPooling>true</StringPooling>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -156,4 +156,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -81,6 +81,7 @@ LOCAL_SRC_FILES :=\
image/png_load.cpp \
thin3d/thin3d.cpp \
thin3d/thin3d_gl.cpp \
thin3d/thin3d_vulkan.cpp \
ui/view.cpp \
ui/viewgroup.cpp \
ui/ui.cpp \
@ -94,7 +95,7 @@ LOCAL_SRC_FILES :=\
LOCAL_CFLAGS := -O3 -DUSING_GLES2 -fsigned-char -fno-strict-aliasing -Wall -Wno-multichar -D__STDC_CONSTANT_MACROS
LOCAL_CPPFLAGS := -fno-exceptions -std=gnu++11 -fno-rtti -Wno-reorder
LOCAL_C_INCLUDES := $(LOCAL_PATH)/ext $(LOCAL_PATH)/ext/libzip
LOCAL_C_INCLUDES := $(LOCAL_PATH)/ext $(LOCAL_PATH)/ext/libzip ..
#Portable native and separate code on android in future is easy you needs add files
#by ($(target_arch_ABI),arquitecture (armeabi-v7a , armeabi , x86 , MIPS)

View File

@ -29,6 +29,13 @@ inline void Uint8x3ToInt4(int i[4], uint32_t u) {
i[3] = 0;
}
inline void Uint8x3ToInt4_Alpha(int i[4], uint32_t u, uint8_t alpha) {
i[0] = ((u >> 0) & 0xFF);
i[1] = ((u >> 8) & 0xFF);
i[2] = ((u >> 16) & 0xFF);
i[3] = alpha;
}
inline void Uint8x3ToFloat4_Alpha(float f[4], uint32_t u, float alpha) {
f[0] = ((u >> 0) & 0xFF) * (1.0f / 255.0f);
f[1] = ((u >> 8) & 0xFF) * (1.0f / 255.0f);
@ -56,6 +63,10 @@ inline void CopyFloat2(float dest[2], const float src[2]) {
memcpy(dest, src, sizeof(float) * 2);
}
inline void CopyFloat3(float dest[3], const float src[3]) {
memcpy(dest, src, sizeof(float) * 3);
}
inline void CopyFloat1To4(float dest[4], const float src) {
dest[0] = src;
dest[1] = 0.0f;
@ -85,4 +96,4 @@ inline void CopyMatrix4x4(float dest[16], const float src[16]) {
inline void ExpandFloat24x3ToFloat4(float dest[4], uint32_t src[3]) {
uint32_t temp[4] = { src[0] << 8, src[1] << 8, src[2] << 8, 0 };
memcpy(dest, temp, sizeof(float) * 4);
}
}

View File

@ -203,23 +203,36 @@ void Matrix4x4::setProjectionD3D(float near_plane, float far_plane, float fov_ho
}
void Matrix4x4::setOrtho(float left, float right, float bottom, float top, float near, float far) {
setIdentity();
empty();
xx = 2.0f / (right - left);
yy = 2.0f / (top - bottom);
zz = 2.0f / (far - near);
wx = -(right + left) / (right - left);
wy = -(top + bottom) / (top - bottom);
wz = -(far + near) / (far - near);
ww = 1.0f;
}
void Matrix4x4::setOrthoD3D(float left, float right, float bottom, float top, float near, float far) {
setIdentity();
empty();
xx = 2.0f / (right - left);
yy = 2.0f / (top - bottom);
zz = 1.0f / (far - near);
wx = -(right + left) / (right - left);
wy = -(top + bottom) / (top - bottom);
wz = -near / (far - near);
ww = 1.0f;
}
void Matrix4x4::setOrthoVulkan(float left, float right, float top, float bottom, float near, float far) {
empty();
xx = 2.0f / (right - left);
yy = 2.0f / (bottom - top);
zz = 1.0f / (far - near);
wx = -(right + left) / (right - left);
wy = -(top + bottom) / (bottom - top);
wz = -near / (far - near);
ww = 1.0f;
}
void Matrix4x4::setProjectionInf(const float near_plane, const float fov_horiz, const float aspect) {

Some files were not shown because too many files have changed in this diff Show More