RenderDoc tasks (#1291)

* First pass on renderdoc support

* Fixes and improvements

* Fix debugging/cleaner code.

* Download from fabric maven

* Fix build

* Revert changes to AbstractRunTask
This commit is contained in:
modmuss
2025-04-26 15:37:46 +01:00
committed by GitHub
parent b09c037007
commit 8014d2c18b
8 changed files with 229 additions and 7 deletions

View File

@@ -17,7 +17,7 @@ jobs:
java-version: 21
distribution: 'temurin'
- uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v2
- uses: gradle/actions/wrapper-validation@v4
# Generate the build number based on tags to allow per branch build numbers, not something github provides by default.
- name: Generate build number

View File

@@ -17,7 +17,7 @@ jobs:
java-version: 21
distribution: 'temurin'
- uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v2
- uses: gradle/actions/wrapper-validation@v4
# Generate the build number based on tags to allow per branch build numbers, not something github provides by default.
- name: Generate build number

View File

@@ -27,7 +27,7 @@ jobs:
java-version: 21
distribution: 'temurin'
- uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v2
- uses: gradle/actions/wrapper-validation@v4
- run: ./gradlew build check -x test --stacktrace --warning-mode fail
build_windows:
@@ -41,7 +41,7 @@ jobs:
with:
java-version: 21
distribution: 'temurin'
- uses: gradle/wrapper-validation-action@v2
- uses: gradle/actions/wrapper-validation@v4
- run: ./gradlew build check -x test --stacktrace --warning-mode fail
# This job is used to feed the test matrix of next job to allow the tests to run in parallel

View File

@@ -12,6 +12,9 @@ jetbrains-annotations = "26.0.2"
native-support = "1.0.1"
fabric-installer = "1.0.3"
# Debug tools
renderdoc = "1.37"
[libraries]
# Decompilers
fernflower = { module = "net.fabricmc:fabric-fernflower", version.ref = "fernflower" }
@@ -24,4 +27,7 @@ dev-launch-injector = { module = "net.fabricmc:dev-launch-injector", version.ref
terminal-console-appender = { module = "net.minecrell:terminalconsoleappender", version.ref = "terminal-console-appender" }
jetbrains-annotations = { module = "org.jetbrains:annotations", version.ref = "jetbrains-annotations" }
native-support = { module = "net.fabricmc:fabric-loom-native-support", version.ref = "native-support" }
fabric-installer = { module = "net.fabricmc:fabric-installer", version.ref = "fabric-installer" }
fabric-installer = { module = "net.fabricmc:fabric-installer", version.ref = "fabric-installer" }
# Debug tools
renderdoc = { module = "org.renderdoc:renderdoc", version.ref = "renderdoc" } # Not a maven dependency

View File

@@ -24,13 +24,18 @@
package net.fabricmc.loom.task;
import java.io.File;
import javax.inject.Inject;
import com.google.common.base.Preconditions;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.file.FileCollection;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Sync;
import org.gradle.api.tasks.TaskContainer;
import org.gradle.api.tasks.TaskOutputs;
import org.gradle.api.tasks.TaskProvider;
import net.fabricmc.loom.LoomGradleExtension;
@@ -41,6 +46,8 @@ import net.fabricmc.loom.task.launch.GenerateDLIConfigTask;
import net.fabricmc.loom.task.launch.GenerateLog4jConfigTask;
import net.fabricmc.loom.task.launch.GenerateRemapClasspathTask;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.LoomVersions;
import net.fabricmc.loom.util.Platform;
import net.fabricmc.loom.util.gradle.GradleUtils;
public abstract class LoomTasks implements Runnable {
@@ -132,17 +139,28 @@ public abstract class LoomTasks implements Runnable {
private void registerRunTasks() {
LoomGradleExtension extension = LoomGradleExtension.get(getProject());
final boolean renderDocSupported = RenderDocRunTask.isSupported(Platform.CURRENT);
Preconditions.checkArgument(extension.getRunConfigs().size() == 0, "Run configurations must not be registered before loom");
extension.getRunConfigs().whenObjectAdded(config -> {
getTasks().register(getRunConfigTaskName(config), RunGameTask.class, config).configure(t -> {
var runTask = getTasks().register(getRunConfigTaskName(config), RunGameTask.class, config);
runTask.configure(t -> {
t.setDescription("Starts the '" + config.getConfigName() + "' run configuration");
t.dependsOn(config.getEnvironment().equals("client") ? "configureClientLaunch" : "configureLaunch");
});
if (config.getName().equals("client") && renderDocSupported) {
getTasks().register("runClientRenderDoc", RenderDocRunTask.class, config);
}
});
if (renderDocSupported) {
configureRenderDocTasks();
}
extension.getRunConfigs().whenObjectRemoved(runConfigSettings -> {
getTasks().named(getRunConfigTaskName(runConfigSettings), task -> {
// Disable the task so it can't be run
@@ -170,7 +188,58 @@ public abstract class LoomTasks implements Runnable {
return;
}
extension.getRunConfigs().removeIf(settings -> settings.getName().equals(taskName));
extension.getRunConfigs().removeIf(settings -> settings.getName().equals(taskName)
|| settings.getName().equals(taskName + "RenderDoc"));
});
}
private void configureRenderDocTasks() {
final Platform.OperatingSystem operatingSystem = Platform.CURRENT.getOperatingSystem();
final String renderDocVersion = LoomVersions.RENDERDOC.version();
final String renderDocBaseName = operatingSystem.isWindows()
? "RenderDoc_%s_64".formatted(renderDocVersion)
: "renderdoc_%s".formatted(renderDocVersion);
final String renderDocFilename = operatingSystem.isWindows()
? "%s.zip".formatted(renderDocBaseName)
: "%s.tar.gz".formatted(renderDocBaseName);
final String renderDocUrl = "https://maven.fabricmc.net/org/renderdoc/%s".formatted(renderDocFilename);
final String executableExt = operatingSystem.isWindows() ? ".exe" : "";
var downloadRenderDoc = getTasks().register("downloadRenderDoc", DownloadTask.class, task -> {
task.setGroup(Constants.TaskGroup.FABRIC);
task.getUrl().set(renderDocUrl);
task.getOutput().set(getProject().getLayout().getBuildDirectory().file(renderDocFilename));
});
var extractRenderDoc = getTasks().register("extractRenderDoc", Sync.class, task -> {
task.setGroup(Constants.TaskGroup.FABRIC);
if (operatingSystem.isWindows()) {
task.from(getProject().zipTree(downloadRenderDoc.map(DownloadTask::getOutput)));
} else {
task.from(getProject().tarTree(downloadRenderDoc.map(DownloadTask::getOutput)));
}
task.into(getProject().getLayout().getBuildDirectory().dir("renderdoc"));
});
Provider<File> renderDocDir = extractRenderDoc.map(Sync::getOutputs)
.map(TaskOutputs::getFiles)
.map(FileCollection::getSingleFile)
.map(dir -> new File(dir, renderDocBaseName));
if (operatingSystem.isLinux()) {
renderDocDir = renderDocDir.map(dir -> new File(dir, "bin"));
}
Provider<File> renderDocCMD = renderDocDir.map(dir -> new File(dir, "renderdoccmd" + executableExt));
Provider<File> renderDocUI = renderDocDir.map(dir -> new File(dir, "qrenderdoc" + executableExt));
getTasks().register("startRenderDocUI", RenderDocRunUITask.class, task -> task.getRenderDocExecutable().fileProvider(renderDocUI));
getTasks().withType(RenderDocRunTask.class).configureEach(task -> {
task.getRenderDocExecutable().fileProvider(renderDocCMD);
});
}

View File

@@ -0,0 +1,92 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2025 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.task;
import java.io.File;
import javax.inject.Inject;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.process.CommandLineArgumentProvider;
import org.gradle.process.ExecOperations;
import org.gradle.process.ExecResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.Platform;
public abstract class RenderDocRunTask extends RunGameTask {
private static final Logger LOGGER = LoggerFactory.getLogger(RenderDocRunTask.class);
@InputFile
public abstract RegularFileProperty getRenderDocExecutable();
@Input
public abstract ListProperty<String> getRenderDocArgs();
@Inject
protected abstract ExecOperations getExecOperations();
@Inject
public RenderDocRunTask(RunConfigSettings settings) {
super(settings);
setGroup(Constants.TaskGroup.FABRIC);
dependsOn("configureClientLaunch");
getRenderDocArgs().addAll("capture", "--wait-for-exit", "--working-dir", getWorkingDir().getAbsolutePath());
}
@Override
public void exec() {
ExecResult result = getExecOperations().exec(exec -> {
exec.workingDir(new File(getProjectDir().get(), getInternalRunDir().get()));
exec.environment(getInternalEnvironmentVars().get());
exec.commandLine(getRenderDocExecutable().get().getAsFile());
exec.args(getRenderDocArgs().get());
exec.args(getJavaLauncher().get().getExecutablePath());
exec.args(getJvmArgs());
exec.args(getMainClass().get());
for (CommandLineArgumentProvider provider : getArgumentProviders()) {
exec.args(provider.asArguments());
}
LOGGER.info("Running command: {}", exec.getCommandLine());
});
result.assertNormalExitValue();
}
public static boolean isSupported(Platform platform) {
final Platform.OperatingSystem os = platform.getOperatingSystem();
final Platform.Architecture arch = platform.getArchitecture();
// RenderDoc does support 32-bit Windows, but I cannot be bothered to test/maintain it
return (os.isLinux() || os.isWindows()) && arch.isX64();
}
}

View File

@@ -0,0 +1,51 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2025 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.task;
import java.io.IOException;
import org.gradle.api.DefaultTask;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.TaskAction;
import net.fabricmc.loom.util.Constants;
public abstract class RenderDocRunUITask extends DefaultTask {
@InputFile
public abstract RegularFileProperty getRenderDocExecutable();
public RenderDocRunUITask() {
setGroup(Constants.TaskGroup.FABRIC);
}
@TaskAction
public void run() throws IOException {
ProcessBuilder builder = new ProcessBuilder()
.command(getRenderDocExecutable().getAsFile().get().getAbsolutePath());
builder.start();
// Allow to run in the background.
}
}

View File

@@ -53,6 +53,10 @@ public interface Platform {
boolean isArm();
boolean isRiscV();
default boolean isX64() {
return is64Bit() && !isArm() && !isRiscV();
}
}
Architecture getArchitecture();