maxwell_3d.cpp 21 KB

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