maxwell_3d.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <cstring>
  4. #include <optional>
  5. #include "common/assert.h"
  6. #include "core/core.h"
  7. #include "core/core_timing.h"
  8. #include "video_core/dirty_flags.h"
  9. #include "video_core/engines/maxwell_3d.h"
  10. #include "video_core/gpu.h"
  11. #include "video_core/memory_manager.h"
  12. #include "video_core/rasterizer_interface.h"
  13. #include "video_core/textures/texture.h"
  14. namespace Tegra::Engines {
  15. using VideoCore::QueryType;
  16. /// First register id that is actually a Macro call.
  17. constexpr u32 MacroRegistersStart = 0xE00;
  18. Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_)
  19. : system{system_}, memory_manager{memory_manager_}, macro_engine{GetMacroEngine(*this)},
  20. upload_state{memory_manager, regs.upload} {
  21. dirty.flags.flip();
  22. InitializeRegisterDefaults();
  23. }
  24. Maxwell3D::~Maxwell3D() = default;
  25. void Maxwell3D::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
  26. rasterizer = rasterizer_;
  27. upload_state.BindRasterizer(rasterizer_);
  28. }
  29. void Maxwell3D::InitializeRegisterDefaults() {
  30. // Initializes registers to their default values - what games expect them to be at boot. This is
  31. // for certain registers that may not be explicitly set by games.
  32. // Reset all registers to zero
  33. std::memset(&regs, 0, sizeof(regs));
  34. // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is
  35. // needed for ARMS.
  36. for (auto& viewport : regs.viewports) {
  37. viewport.depth_range_near = 0.0f;
  38. viewport.depth_range_far = 1.0f;
  39. }
  40. for (auto& viewport : regs.viewport_transform) {
  41. viewport.swizzle.x.Assign(Regs::ViewportSwizzle::PositiveX);
  42. viewport.swizzle.y.Assign(Regs::ViewportSwizzle::PositiveY);
  43. viewport.swizzle.z.Assign(Regs::ViewportSwizzle::PositiveZ);
  44. viewport.swizzle.w.Assign(Regs::ViewportSwizzle::PositiveW);
  45. }
  46. // Doom and Bomberman seems to use the uninitialized registers and just enable blend
  47. // so initialize blend registers with sane values
  48. regs.blend.color_op = Regs::Blend::Equation::Add_D3D;
  49. regs.blend.color_source = Regs::Blend::Factor::One_D3D;
  50. regs.blend.color_dest = Regs::Blend::Factor::Zero_D3D;
  51. regs.blend.alpha_op = Regs::Blend::Equation::Add_D3D;
  52. regs.blend.alpha_source = Regs::Blend::Factor::One_D3D;
  53. regs.blend.alpha_dest = Regs::Blend::Factor::Zero_D3D;
  54. for (auto& blend : regs.blend_per_target) {
  55. blend.color_op = Regs::Blend::Equation::Add_D3D;
  56. blend.color_source = Regs::Blend::Factor::One_D3D;
  57. blend.color_dest = Regs::Blend::Factor::Zero_D3D;
  58. blend.alpha_op = Regs::Blend::Equation::Add_D3D;
  59. blend.alpha_source = Regs::Blend::Factor::One_D3D;
  60. blend.alpha_dest = Regs::Blend::Factor::Zero_D3D;
  61. }
  62. regs.stencil_front_op.fail = Regs::StencilOp::Op::Keep_D3D;
  63. regs.stencil_front_op.zfail = Regs::StencilOp::Op::Keep_D3D;
  64. regs.stencil_front_op.zpass = Regs::StencilOp::Op::Keep_D3D;
  65. regs.stencil_front_op.func = Regs::ComparisonOp::Always_GL;
  66. regs.stencil_front_func_mask = 0xFFFFFFFF;
  67. regs.stencil_front_mask = 0xFFFFFFFF;
  68. regs.stencil_two_side_enable = 1;
  69. regs.stencil_back_op.fail = Regs::StencilOp::Op::Keep_D3D;
  70. regs.stencil_back_op.zfail = Regs::StencilOp::Op::Keep_D3D;
  71. regs.stencil_back_op.zpass = Regs::StencilOp::Op::Keep_D3D;
  72. regs.stencil_back_op.func = Regs::ComparisonOp::Always_GL;
  73. regs.stencil_back_func_mask = 0xFFFFFFFF;
  74. regs.stencil_back_mask = 0xFFFFFFFF;
  75. regs.depth_test_func = Regs::ComparisonOp::Always_GL;
  76. regs.gl_front_face = Regs::FrontFace::CounterClockWise;
  77. regs.gl_cull_face = Regs::CullFace::Back;
  78. // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
  79. // register carrying a default value. Assume it's OpenGL's default (1).
  80. regs.point_size = 1.0f;
  81. // TODO(bunnei): Some games do not initialize the color masks (e.g. Sonic Mania). Assuming a
  82. // default of enabled fixes rendering here.
  83. for (auto& color_mask : regs.color_mask) {
  84. color_mask.R.Assign(1);
  85. color_mask.G.Assign(1);
  86. color_mask.B.Assign(1);
  87. color_mask.A.Assign(1);
  88. }
  89. for (auto& format : regs.vertex_attrib_format) {
  90. format.constant.Assign(1);
  91. }
  92. // NVN games expect these values to be enabled at boot
  93. regs.rasterize_enable = 1;
  94. regs.color_target_mrt_enable = 1;
  95. regs.framebuffer_srgb = 1;
  96. regs.line_width_aliased = 1.0f;
  97. regs.line_width_smooth = 1.0f;
  98. regs.gl_front_face = Maxwell3D::Regs::FrontFace::ClockWise;
  99. regs.polygon_mode_back = Maxwell3D::Regs::PolygonMode::Fill;
  100. regs.polygon_mode_front = Maxwell3D::Regs::PolygonMode::Fill;
  101. shadow_state = regs;
  102. draw_command[MAXWELL3D_REG_INDEX(draw.end)] = true;
  103. draw_command[MAXWELL3D_REG_INDEX(draw.begin)] = true;
  104. draw_command[MAXWELL3D_REG_INDEX(vertex_buffer.first)] = true;
  105. draw_command[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true;
  106. draw_command[MAXWELL3D_REG_INDEX(index_buffer.first)] = true;
  107. draw_command[MAXWELL3D_REG_INDEX(index_buffer.count)] = true;
  108. draw_command[MAXWELL3D_REG_INDEX(draw_inline_index)] = true;
  109. draw_command[MAXWELL3D_REG_INDEX(inline_index_2x16.even)] = true;
  110. draw_command[MAXWELL3D_REG_INDEX(inline_index_4x8.index0)] = true;
  111. }
  112. void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
  113. if (executing_macro == 0) {
  114. // A macro call must begin by writing the macro method's register, not its argument.
  115. ASSERT_MSG((method % 2) == 0,
  116. "Can't start macro execution by writing to the ARGS register");
  117. executing_macro = method;
  118. }
  119. macro_params.insert(macro_params.end(), base_start, base_start + amount);
  120. // Call the macro when there are no more parameters in the command buffer
  121. if (is_last_call) {
  122. CallMacroMethod(executing_macro, macro_params);
  123. macro_params.clear();
  124. }
  125. }
  126. u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
  127. // Keep track of the register value in shadow_state when requested.
  128. const auto control = shadow_state.shadow_ram_control;
  129. if (control == Regs::ShadowRamControl::Track ||
  130. control == Regs::ShadowRamControl::TrackWithFilter) {
  131. shadow_state.reg_array[method] = argument;
  132. return argument;
  133. }
  134. if (control == Regs::ShadowRamControl::Replay) {
  135. return shadow_state.reg_array[method];
  136. }
  137. return argument;
  138. }
  139. void Maxwell3D::ProcessDirtyRegisters(u32 method, u32 argument) {
  140. if (regs.reg_array[method] == argument) {
  141. return;
  142. }
  143. regs.reg_array[method] = argument;
  144. for (const auto& table : dirty.tables) {
  145. dirty.flags[table[method]] = true;
  146. }
  147. }
  148. void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument,
  149. bool is_last_call) {
  150. switch (method) {
  151. case MAXWELL3D_REG_INDEX(wait_for_idle):
  152. return rasterizer->WaitForIdle();
  153. case MAXWELL3D_REG_INDEX(shadow_ram_control):
  154. shadow_state.shadow_ram_control = static_cast<Regs::ShadowRamControl>(nonshadow_argument);
  155. return;
  156. case MAXWELL3D_REG_INDEX(load_mme.instruction_ptr):
  157. return macro_engine->ClearCode(regs.load_mme.instruction_ptr);
  158. case MAXWELL3D_REG_INDEX(load_mme.instruction):
  159. return macro_engine->AddCode(regs.load_mme.instruction_ptr, argument);
  160. case MAXWELL3D_REG_INDEX(load_mme.start_address):
  161. return ProcessMacroBind(argument);
  162. case MAXWELL3D_REG_INDEX(falcon[4]):
  163. return ProcessFirmwareCall4();
  164. case MAXWELL3D_REG_INDEX(const_buffer.buffer):
  165. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 1:
  166. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 2:
  167. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 3:
  168. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 4:
  169. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 5:
  170. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 6:
  171. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 7:
  172. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 8:
  173. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 9:
  174. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 10:
  175. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 11:
  176. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 12:
  177. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 13:
  178. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 14:
  179. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15:
  180. return ProcessCBData(argument);
  181. case MAXWELL3D_REG_INDEX(bind_groups[0].raw_config):
  182. return ProcessCBBind(0);
  183. case MAXWELL3D_REG_INDEX(bind_groups[1].raw_config):
  184. return ProcessCBBind(1);
  185. case MAXWELL3D_REG_INDEX(bind_groups[2].raw_config):
  186. return ProcessCBBind(2);
  187. case MAXWELL3D_REG_INDEX(bind_groups[3].raw_config):
  188. return ProcessCBBind(3);
  189. case MAXWELL3D_REG_INDEX(bind_groups[4].raw_config):
  190. return ProcessCBBind(4);
  191. case MAXWELL3D_REG_INDEX(index_buffer32_first):
  192. regs.index_buffer.count = regs.index_buffer32_first.count;
  193. regs.index_buffer.first = regs.index_buffer32_first.first;
  194. dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
  195. return ProcessDraw();
  196. case MAXWELL3D_REG_INDEX(index_buffer16_first):
  197. regs.index_buffer.count = regs.index_buffer16_first.count;
  198. regs.index_buffer.first = regs.index_buffer16_first.first;
  199. dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
  200. return ProcessDraw();
  201. case MAXWELL3D_REG_INDEX(index_buffer8_first):
  202. regs.index_buffer.count = regs.index_buffer8_first.count;
  203. regs.index_buffer.first = regs.index_buffer8_first.first;
  204. dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
  205. return ProcessDraw();
  206. case MAXWELL3D_REG_INDEX(topology_override):
  207. use_topology_override = true;
  208. return;
  209. case MAXWELL3D_REG_INDEX(clear_surface):
  210. return ProcessClearBuffers();
  211. case MAXWELL3D_REG_INDEX(report_semaphore.query):
  212. return ProcessQueryGet();
  213. case MAXWELL3D_REG_INDEX(render_enable.mode):
  214. return ProcessQueryCondition();
  215. case MAXWELL3D_REG_INDEX(clear_report_value):
  216. return ProcessCounterReset();
  217. case MAXWELL3D_REG_INDEX(sync_info):
  218. return ProcessSyncPoint();
  219. case MAXWELL3D_REG_INDEX(launch_dma):
  220. return upload_state.ProcessExec(regs.launch_dma.memory_layout.Value() ==
  221. Regs::LaunchDMA::Layout::Pitch);
  222. case MAXWELL3D_REG_INDEX(inline_data):
  223. upload_state.ProcessData(argument, is_last_call);
  224. return;
  225. case MAXWELL3D_REG_INDEX(fragment_barrier):
  226. return rasterizer->FragmentBarrier();
  227. }
  228. }
  229. void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) {
  230. // Reset the current macro.
  231. executing_macro = 0;
  232. // Lookup the macro offset
  233. const u32 entry =
  234. ((method - MacroRegistersStart) >> 1) % static_cast<u32>(macro_positions.size());
  235. // Execute the current macro.
  236. macro_engine->Execute(macro_positions[entry], parameters);
  237. ProcessDeferredDraw();
  238. }
  239. void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
  240. // It is an error to write to a register other than the current macro's ARG register before
  241. // it has finished execution.
  242. if (executing_macro != 0) {
  243. ASSERT(method == executing_macro + 1);
  244. }
  245. // Methods after 0xE00 are special, they're actually triggers for some microcode that was
  246. // uploaded to the GPU during initialization.
  247. if (method >= MacroRegistersStart) {
  248. ProcessMacro(method, &method_argument, 1, is_last_call);
  249. return;
  250. }
  251. ASSERT_MSG(method < Regs::NUM_REGS,
  252. "Invalid Maxwell3D register, increase the size of the Regs structure");
  253. if (draw_command[method]) {
  254. regs.reg_array[method] = method_argument;
  255. deferred_draw_method.push_back(method);
  256. auto u32_to_u8 = [&](const u32 argument) {
  257. inline_index_draw_indexes.push_back(static_cast<u8>(argument & 0x000000ff));
  258. inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0x0000ff00) >> 8));
  259. inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0x00ff0000) >> 16));
  260. inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0xff000000) >> 24));
  261. };
  262. if (MAXWELL3D_REG_INDEX(draw_inline_index) == method) {
  263. u32_to_u8(method_argument);
  264. } else if (MAXWELL3D_REG_INDEX(inline_index_2x16.even) == method) {
  265. u32_to_u8(regs.inline_index_2x16.even);
  266. u32_to_u8(regs.inline_index_2x16.odd);
  267. } else if (MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == method) {
  268. u32_to_u8(regs.inline_index_4x8.index0);
  269. u32_to_u8(regs.inline_index_4x8.index1);
  270. u32_to_u8(regs.inline_index_4x8.index2);
  271. u32_to_u8(regs.inline_index_4x8.index3);
  272. }
  273. } else {
  274. ProcessDeferredDraw();
  275. const u32 argument = ProcessShadowRam(method, method_argument);
  276. ProcessDirtyRegisters(method, argument);
  277. ProcessMethodCall(method, argument, method_argument, is_last_call);
  278. }
  279. }
  280. void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
  281. u32 methods_pending) {
  282. // Methods after 0xE00 are special, they're actually triggers for some microcode that was
  283. // uploaded to the GPU during initialization.
  284. if (method >= MacroRegistersStart) {
  285. ProcessMacro(method, base_start, amount, amount == methods_pending);
  286. return;
  287. }
  288. switch (method) {
  289. case MAXWELL3D_REG_INDEX(const_buffer.buffer):
  290. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 1:
  291. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 2:
  292. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 3:
  293. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 4:
  294. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 5:
  295. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 6:
  296. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 7:
  297. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 8:
  298. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 9:
  299. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 10:
  300. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 11:
  301. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 12:
  302. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 13:
  303. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 14:
  304. case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15:
  305. ProcessCBMultiData(base_start, amount);
  306. break;
  307. case MAXWELL3D_REG_INDEX(inline_data):
  308. upload_state.ProcessData(base_start, static_cast<size_t>(amount));
  309. return;
  310. default:
  311. for (std::size_t i = 0; i < amount; i++) {
  312. CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1);
  313. }
  314. break;
  315. }
  316. }
  317. void Maxwell3D::ProcessTopologyOverride() {
  318. using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology;
  319. using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride;
  320. PrimitiveTopology topology{};
  321. switch (regs.topology_override) {
  322. case PrimitiveTopologyOverride::None:
  323. topology = regs.draw.topology;
  324. break;
  325. case PrimitiveTopologyOverride::Points:
  326. topology = PrimitiveTopology::Points;
  327. break;
  328. case PrimitiveTopologyOverride::Lines:
  329. topology = PrimitiveTopology::Lines;
  330. break;
  331. case PrimitiveTopologyOverride::LineStrip:
  332. topology = PrimitiveTopology::LineStrip;
  333. break;
  334. default:
  335. topology = static_cast<PrimitiveTopology>(regs.topology_override);
  336. break;
  337. }
  338. if (use_topology_override) {
  339. regs.draw.topology.Assign(topology);
  340. }
  341. }
  342. void Maxwell3D::ProcessMacroUpload(u32 data) {
  343. macro_engine->AddCode(regs.load_mme.instruction_ptr++, data);
  344. }
  345. void Maxwell3D::ProcessMacroBind(u32 data) {
  346. macro_positions[regs.load_mme.start_address_ptr++] = data;
  347. }
  348. void Maxwell3D::ProcessFirmwareCall4() {
  349. LOG_WARNING(HW_GPU, "(STUBBED) called");
  350. // Firmware call 4 is a blob that changes some registers depending on its parameters.
  351. // These registers don't affect emulation and so are stubbed by setting 0xd00 to 1.
  352. regs.shadow_scratch[0] = 1;
  353. }
  354. void Maxwell3D::StampQueryResult(u64 payload, bool long_query) {
  355. const GPUVAddr sequence_address{regs.report_semaphore.Address()};
  356. if (long_query) {
  357. memory_manager.Write<u64>(sequence_address + sizeof(u64), system.GPU().GetTicks());
  358. memory_manager.Write<u64>(sequence_address, payload);
  359. } else {
  360. memory_manager.Write<u32>(sequence_address, static_cast<u32>(payload));
  361. }
  362. }
  363. void Maxwell3D::ProcessQueryGet() {
  364. // TODO(Subv): Support the other query units.
  365. if (regs.report_semaphore.query.location != Regs::ReportSemaphore::Location::All) {
  366. LOG_DEBUG(HW_GPU, "Locations other than ALL are unimplemented");
  367. }
  368. switch (regs.report_semaphore.query.operation) {
  369. case Regs::ReportSemaphore::Operation::Release:
  370. if (regs.report_semaphore.query.short_query != 0) {
  371. const GPUVAddr sequence_address{regs.report_semaphore.Address()};
  372. const u32 payload = regs.report_semaphore.payload;
  373. std::function<void()> operation([this, sequence_address, payload] {
  374. memory_manager.Write<u32>(sequence_address, payload);
  375. });
  376. rasterizer->SignalFence(std::move(operation));
  377. } else {
  378. struct LongQueryResult {
  379. u64_le value;
  380. u64_le timestamp;
  381. };
  382. const GPUVAddr sequence_address{regs.report_semaphore.Address()};
  383. const u32 payload = regs.report_semaphore.payload;
  384. [this, sequence_address, payload] {
  385. memory_manager.Write<u64>(sequence_address + sizeof(u64), system.GPU().GetTicks());
  386. memory_manager.Write<u64>(sequence_address, payload);
  387. }();
  388. }
  389. break;
  390. case Regs::ReportSemaphore::Operation::Acquire:
  391. // TODO(Blinkhawk): Under this operation, the GPU waits for the CPU to write a value that
  392. // matches the current payload.
  393. UNIMPLEMENTED_MSG("Unimplemented query operation ACQUIRE");
  394. break;
  395. case Regs::ReportSemaphore::Operation::ReportOnly:
  396. if (const std::optional<u64> result = GetQueryResult()) {
  397. // If the query returns an empty optional it means it's cached and deferred.
  398. // In this case we have a non-empty result, so we stamp it immediately.
  399. StampQueryResult(*result, regs.report_semaphore.query.short_query == 0);
  400. }
  401. break;
  402. case Regs::ReportSemaphore::Operation::Trap:
  403. UNIMPLEMENTED_MSG("Unimplemented query operation TRAP");
  404. break;
  405. default:
  406. UNIMPLEMENTED_MSG("Unknown query operation");
  407. break;
  408. }
  409. }
  410. void Maxwell3D::ProcessQueryCondition() {
  411. const GPUVAddr condition_address{regs.render_enable.Address()};
  412. switch (regs.render_enable.mode) {
  413. case Regs::RenderEnable::Mode::True: {
  414. execute_on = true;
  415. break;
  416. }
  417. case Regs::RenderEnable::Mode::False: {
  418. execute_on = false;
  419. break;
  420. }
  421. case Regs::RenderEnable::Mode::Conditional: {
  422. Regs::ReportSemaphore::Compare cmp;
  423. memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
  424. execute_on = cmp.initial_sequence != 0U && cmp.initial_mode != 0U;
  425. break;
  426. }
  427. case Regs::RenderEnable::Mode::IfEqual: {
  428. Regs::ReportSemaphore::Compare cmp;
  429. memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
  430. execute_on =
  431. cmp.initial_sequence == cmp.current_sequence && cmp.initial_mode == cmp.current_mode;
  432. break;
  433. }
  434. case Regs::RenderEnable::Mode::IfNotEqual: {
  435. Regs::ReportSemaphore::Compare cmp;
  436. memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
  437. execute_on =
  438. cmp.initial_sequence != cmp.current_sequence || cmp.initial_mode != cmp.current_mode;
  439. break;
  440. }
  441. default: {
  442. UNIMPLEMENTED_MSG("Uninplemented Condition Mode!");
  443. execute_on = true;
  444. break;
  445. }
  446. }
  447. }
  448. void Maxwell3D::ProcessCounterReset() {
  449. switch (regs.clear_report_value) {
  450. case Regs::ClearReport::ZPassPixelCount:
  451. rasterizer->ResetCounter(QueryType::SamplesPassed);
  452. break;
  453. default:
  454. LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}", regs.clear_report_value);
  455. break;
  456. }
  457. }
  458. void Maxwell3D::ProcessSyncPoint() {
  459. const u32 sync_point = regs.sync_info.sync_point.Value();
  460. const u32 cache_flush = regs.sync_info.clean_l2.Value();
  461. if (cache_flush != 0) {
  462. rasterizer->InvalidateGPUCache();
  463. }
  464. rasterizer->SignalSyncPoint(sync_point);
  465. }
  466. std::optional<u64> Maxwell3D::GetQueryResult() {
  467. switch (regs.report_semaphore.query.report) {
  468. case Regs::ReportSemaphore::Report::Payload:
  469. return regs.report_semaphore.payload;
  470. case Regs::ReportSemaphore::Report::ZPassPixelCount64:
  471. // Deferred.
  472. rasterizer->Query(regs.report_semaphore.Address(), QueryType::SamplesPassed,
  473. system.GPU().GetTicks());
  474. return std::nullopt;
  475. default:
  476. LOG_DEBUG(HW_GPU, "Unimplemented query report type {}",
  477. regs.report_semaphore.query.report.Value());
  478. return 1;
  479. }
  480. }
  481. void Maxwell3D::ProcessCBBind(size_t stage_index) {
  482. // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage.
  483. const auto& bind_data = regs.bind_groups[stage_index];
  484. auto& buffer = state.shader_stages[stage_index].const_buffers[bind_data.shader_slot];
  485. buffer.enabled = bind_data.valid.Value() != 0;
  486. buffer.address = regs.const_buffer.Address();
  487. buffer.size = regs.const_buffer.size;
  488. const bool is_enabled = bind_data.valid.Value() != 0;
  489. if (!is_enabled) {
  490. rasterizer->DisableGraphicsUniformBuffer(stage_index, bind_data.shader_slot);
  491. return;
  492. }
  493. const GPUVAddr gpu_addr = regs.const_buffer.Address();
  494. const u32 size = regs.const_buffer.size;
  495. rasterizer->BindGraphicsUniformBuffer(stage_index, bind_data.shader_slot, gpu_addr, size);
  496. }
  497. void Maxwell3D::ProcessCBMultiData(const u32* start_base, u32 amount) {
  498. // Write the input value to the current const buffer at the current position.
  499. const GPUVAddr buffer_address = regs.const_buffer.Address();
  500. ASSERT(buffer_address != 0);
  501. // Don't allow writing past the end of the buffer.
  502. ASSERT(regs.const_buffer.offset <= regs.const_buffer.size);
  503. const GPUVAddr address{buffer_address + regs.const_buffer.offset};
  504. const size_t copy_size = amount * sizeof(u32);
  505. memory_manager.WriteBlock(address, start_base, copy_size);
  506. // Increment the current buffer position.
  507. regs.const_buffer.offset += static_cast<u32>(copy_size);
  508. }
  509. void Maxwell3D::ProcessCBData(u32 value) {
  510. ProcessCBMultiData(&value, 1);
  511. }
  512. Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
  513. const GPUVAddr tic_address_gpu{regs.tex_header.Address() +
  514. tic_index * sizeof(Texture::TICEntry)};
  515. Texture::TICEntry tic_entry;
  516. memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry));
  517. return tic_entry;
  518. }
  519. Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const {
  520. const GPUVAddr tsc_address_gpu{regs.tex_sampler.Address() +
  521. tsc_index * sizeof(Texture::TSCEntry)};
  522. Texture::TSCEntry tsc_entry;
  523. memory_manager.ReadBlockUnsafe(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry));
  524. return tsc_entry;
  525. }
  526. u32 Maxwell3D::GetRegisterValue(u32 method) const {
  527. ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Maxwell3D register");
  528. return regs.reg_array[method];
  529. }
  530. void Maxwell3D::ProcessClearBuffers() {
  531. rasterizer->Clear();
  532. }
  533. void Maxwell3D::ProcessDraw(u32 instance_count) {
  534. LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
  535. regs.vertex_buffer.count);
  536. ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?");
  537. // Both instance configuration registers can not be set at the same time.
  538. ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First ||
  539. regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged,
  540. "Illegal combination of instancing parameters");
  541. ProcessTopologyOverride();
  542. const bool is_indexed = regs.index_buffer.count && !regs.vertex_buffer.count;
  543. if (ShouldExecute()) {
  544. rasterizer->Draw(is_indexed, instance_count);
  545. }
  546. if (is_indexed) {
  547. regs.index_buffer.count = 0;
  548. } else {
  549. regs.vertex_buffer.count = 0;
  550. }
  551. }
  552. void Maxwell3D::ProcessDeferredDraw() {
  553. if (deferred_draw_method.empty()) {
  554. return;
  555. }
  556. enum class DrawMode {
  557. Undefined,
  558. General,
  559. Instance,
  560. };
  561. DrawMode draw_mode{DrawMode::Undefined};
  562. u32 method_count = static_cast<u32>(deferred_draw_method.size());
  563. u32 method = deferred_draw_method[method_count - 1];
  564. if (MAXWELL3D_REG_INDEX(draw.end) != method) {
  565. return;
  566. }
  567. draw_mode = (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
  568. (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged)
  569. ? DrawMode::Instance
  570. : DrawMode::General;
  571. u32 instance_count = 0;
  572. if (draw_mode == DrawMode::Instance) {
  573. u32 vertex_buffer_count = 0;
  574. u32 index_buffer_count = 0;
  575. for (u32 index = 0; index < method_count; ++index) {
  576. method = deferred_draw_method[index];
  577. if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count)) {
  578. instance_count = ++vertex_buffer_count;
  579. } else if (method == MAXWELL3D_REG_INDEX(index_buffer.count)) {
  580. instance_count = ++index_buffer_count;
  581. }
  582. }
  583. ASSERT_MSG(!(vertex_buffer_count && index_buffer_count),
  584. "Instance both indexed and direct?");
  585. } else {
  586. instance_count = 1;
  587. for (u32 index = 0; index < method_count; ++index) {
  588. method = deferred_draw_method[index];
  589. if (MAXWELL3D_REG_INDEX(draw_inline_index) == method ||
  590. MAXWELL3D_REG_INDEX(inline_index_2x16.even) == method ||
  591. MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == method) {
  592. regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4);
  593. regs.index_buffer.format = Regs::IndexFormat::UnsignedInt;
  594. break;
  595. }
  596. }
  597. }
  598. ProcessDraw(instance_count);
  599. deferred_draw_method.clear();
  600. inline_index_draw_indexes.clear();
  601. }
  602. } // namespace Tegra::Engines