Quellcode durchsuchen

Implemented fully lazy compilation with objects kept from past versions.

David Piuva vor 3 Jahren
Ursprung
Commit
47c9b52c3d

+ 88 - 25
Source/tools/builder/generator.cpp

@@ -3,6 +3,35 @@
 
 
 using namespace dsr;
 using namespace dsr;
 
 
+static uint64_t checksum(const ReadableString& text) {
+	uint64_t a = 0x8C2A03D4;
+	uint64_t b = 0xF42B1583;
+	uint64_t c = 0xA6815E74;
+	uint64_t d = 0;
+	for (int i = 0; i < string_length(text); i++) {
+		a = (b * c + ((i * 3756 + 2654) & 58043)) & 0xFFFFFFFF;
+		b = (231 + text[i] * (a & 154) + c * 867 + 28294061) & 0xFFFFFFFF;
+		c = (a ^ b ^ (text[i] * 1543217521)) & 0xFFFFFFFF;
+		d = d ^ (a << 32) ^ b ^ (c << 16);
+	}
+	return d;
+}
+
+static uint64_t checksum(const Buffer& buffer) {
+	SafePointer<uint8_t> data = buffer_getSafeData<uint8_t>(buffer, "checksum input buffer");
+	uint64_t a = 0x8C2A03D4;
+	uint64_t b = 0xF42B1583;
+	uint64_t c = 0xA6815E74;
+	uint64_t d = 0;
+	for (int i = 0; i < buffer_getSize(buffer); i++) {
+		a = (b * c + ((i * 3756 + 2654) & 58043)) & 0xFFFFFFFF;
+		b = (231 + data[i] * (a & 154) + c * 867 + 28294061) & 0xFFFFFFFF;
+		c = (a ^ b ^ (data[i] * 1543217521)) & 0xFFFFFFFF;
+		d = d ^ (a << 32) ^ b ^ (c << 16);
+	}
+	return d;
+}
+
 struct Connection {
 struct Connection {
 	String path;
 	String path;
 	int64_t lineNumber = -1;
 	int64_t lineNumber = -1;
@@ -34,10 +63,12 @@ static Extension extensionFromString(const ReadableString& extensionName) {
 struct Dependency {
 struct Dependency {
 	String path;
 	String path;
 	Extension extension;
 	Extension extension;
+	uint64_t contentChecksum;
+	bool visited; // Used to avoid infinite loops while traversing dependencies.
 	List<Connection> links; // Depends on having these linked after compiling.
 	List<Connection> links; // Depends on having these linked after compiling.
 	List<Connection> includes; // Depends on having these included in pre-processing.
 	List<Connection> includes; // Depends on having these included in pre-processing.
-	Dependency(const ReadableString& path, Extension extension)
-	: path(path), extension(extension) {}
+	Dependency(const ReadableString& path, Extension extension, uint64_t contentChecksum)
+	: path(path), extension(extension), contentChecksum(contentChecksum) {}
 };
 };
 List<Dependency> dependencies;
 List<Dependency> dependencies;
 
 
@@ -166,8 +197,13 @@ void analyzeFromFile(const ReadableString& absolutePath) {
 	if (lastDotIndex != -1) {
 	if (lastDotIndex != -1) {
 		Extension extension = extensionFromString(string_after(absolutePath, lastDotIndex));
 		Extension extension = extensionFromString(string_after(absolutePath, lastDotIndex));
 		if (extension != Extension::Unknown) {
 		if (extension != Extension::Unknown) {
+			// The old length will be the new dependency's index.
 			int64_t parentIndex = dependencies.length();
 			int64_t parentIndex = dependencies.length();
-			dependencies.pushConstruct(absolutePath, extension);
+			// Get the file's binary content.
+			Buffer fileBuffer = file_loadBuffer(absolutePath);
+			// Get the checksum
+			uint64_t contentChecksum = checksum(fileBuffer);
+			dependencies.pushConstruct(absolutePath, extension, contentChecksum);
 			if (extension == Extension::H || extension == Extension::Hpp) {
 			if (extension == Extension::H || extension == Extension::Hpp) {
 				// The current file is a header, so look for an implementation with the corresponding name.
 				// The current file is a header, so look for an implementation with the corresponding name.
 				String sourcePath = findSourceFile(absolutePath, extension == Extension::H, true);
 				String sourcePath = findSourceFile(absolutePath, extension == Extension::H, true);
@@ -179,9 +215,6 @@ void analyzeFromFile(const ReadableString& absolutePath) {
 					analyzeFromFile(sourcePath);
 					analyzeFromFile(sourcePath);
 				}
 				}
 			}
 			}
-			// Get the file's binary content for checksums.
-			Buffer fileBuffer = file_loadBuffer(absolutePath);
-			// TODO: Get a checksum of fileBuffer and compare with the previous state. Files that changed should recompile all object files that depend on it.
 			// Interpret the file's content.
 			// Interpret the file's content.
 			analyzeCode(parentIndex, string_loadFromMemory(fileBuffer), file_getRelativeParentFolder(absolutePath));
 			analyzeCode(parentIndex, string_loadFromMemory(fileBuffer), file_getRelativeParentFolder(absolutePath));
 		}
 		}
@@ -236,30 +269,42 @@ static void script_executeLocalBinary(String &output, ScriptLanguage language, c
 	}
 	}
 }
 }
 
 
-// TODO: Make a checksum for binary buffers too, so that changes can be detected in a dependency graph for lazy compilation.
-static uint64_t checksum(const ReadableString& text) {
-	uint64_t a = 0x8C2A03D4;
-	uint64_t b = 0xF42B1583;
-	uint64_t c = 0xA6815E74;
-	uint64_t d = 0;
-	for (int i = 0; i < string_length(text); i++) {
-		a = (b * c + ((i * 3756 + 2654) & 58043)) & 0xFFFFFFFF;
-		b = (231 + text[i] * (a & 154) + c * 867 + 28294061) & 0xFFFFFFFF;
-		c = (a ^ b ^ (text[i] * 1543217521)) & 0xFFFFFFFF;
-		d = d ^ (a << 32) ^ b ^ (c << 16);
+static void traverserHeaderChecksums(uint64_t &target, int64_t dependencyIndex) {
+	// Use checksums from headers
+	for (int h = 0; h < dependencies[dependencyIndex].includes.length(); h++) {
+		int64_t includedIndex = dependencies[dependencyIndex].includes[h].dependencyIndex;
+		if (!dependencies[includedIndex].visited) {
+			//printText(U"	traverserHeaderChecksums(", includedIndex, U") ", dependencies[includedIndex].path, "\n");
+			// Bitwise exclusive or is both order independent and entropy preserving for non-repeated content.
+			target = target ^ dependencies[includedIndex].contentChecksum;
+			// Just have to make sure that the same checksum is not used twice.
+			dependencies[includedIndex].visited = true;
+			// Use checksums from headers recursively
+			traverserHeaderChecksums(target, includedIndex);
+		}
 	}
 	}
-	return d;
+}
+
+static uint64_t getCombinedChecksum(int64_t dependencyIndex) {
+	//printText(U"getCombinedChecksum(", dependencyIndex, U") ", dependencies[dependencyIndex].path, "\n");
+	for (int d = 0; d < dependencies.length(); d++) {
+		dependencies[d].visited = false;
+	}
+	dependencies[dependencyIndex].visited = true;
+	uint64_t result = dependencies[dependencyIndex].contentChecksum;
+	traverserHeaderChecksums(result, dependencyIndex);
+	return result;
 }
 }
 
 
 struct SourceObject {
 struct SourceObject {
-	// TODO: Assert that there are no name collisions between identity checksums.
 	uint64_t identityChecksum = 0; // Identification number for the object's name.
 	uint64_t identityChecksum = 0; // Identification number for the object's name.
-	// TODO: Content checksum, dependency checksum.
+	uint64_t combinedChecksum = 0; // Combined content of the source file and all included headers recursively.
 	String sourcePath, objectPath;
 	String sourcePath, objectPath;
-	SourceObject(const ReadableString& sourcePath, const ReadableString& tempFolder, const ReadableString& identity)
-	: identityChecksum(checksum(identity)), sourcePath(sourcePath) {
-		// TODO: Include compiler flags in the checksum.
-		this->objectPath = file_combinePaths(tempFolder, string_combine(U"dfpsr_builder_", identityChecksum, U".o"));
+	SourceObject(const ReadableString& sourcePath, const ReadableString& tempFolder, const ReadableString& identity, int64_t dependencyIndex)
+	: identityChecksum(checksum(identity)), combinedChecksum(getCombinedChecksum(dependencyIndex)), sourcePath(sourcePath) {
+		// By making the content checksum a part of the name, one can switch back to an older version without having to recompile everything again.
+		// Just need to clean the temporary folder once in a while because old versions can take a lot of space.
+		this->objectPath = file_combinePaths(tempFolder, string_combine(U"dfpsr_", this->identityChecksum, U"_", this->combinedChecksum, U".o"));
 	}
 	}
 };
 };
 
 
@@ -319,7 +364,8 @@ void generateCompilationScript(const Machine &settings, const ReadableString& pr
 		if (extension == Extension::C || extension == Extension::Cpp) {
 		if (extension == Extension::C || extension == Extension::Cpp) {
 			// Dependency paths are already absolute from the recursive search.
 			// Dependency paths are already absolute from the recursive search.
 			String sourcePath = dependencies[d].path;
 			String sourcePath = dependencies[d].path;
-			sourceObjects.pushConstruct(sourcePath, tempFolder, string_combine(sourcePath, compilerFlags, projectPath));
+			String identity = string_combine(sourcePath, compilerFlags, projectPath);
+			sourceObjects.pushConstruct(sourcePath, tempFolder, identity, d);
 			if (file_getEntryType(sourcePath) != EntryType::File) {
 			if (file_getEntryType(sourcePath) != EntryType::File) {
 				throwError(U"The source file ", sourcePath, U" could not be found!\n");
 				throwError(U"The source file ", sourcePath, U" could not be found!\n");
 			} else {
 			} else {
@@ -339,8 +385,25 @@ void generateCompilationScript(const Machine &settings, const ReadableString& pr
 		}
 		}
 		String allObjects;
 		String allObjects;
 		for (int i = 0; i < sourceObjects.length(); i++) {
 		for (int i = 0; i < sourceObjects.length(); i++) {
+			if (language == ScriptLanguage::Batch) {
+				string_append(output,  U"if exist ", sourceObjects[i].objectPath, U" (\n");
+			} else if (language == ScriptLanguage::Bash) {
+				string_append(output, U"if [ -e \"", sourceObjects[i].objectPath, U"\" ]; then\n");
+			}
+			script_printMessage(output, language, string_combine(U"Reusing ", sourceObjects[i].sourcePath, U" ID:", sourceObjects[i].identityChecksum, U"."));
+			if (language == ScriptLanguage::Batch) {
+				string_append(output,  U") else (\n");
+			} else if (language == ScriptLanguage::Bash) {
+				string_append(output, U"else\n");
+			}
 			script_printMessage(output, language, string_combine(U"Compiling ", sourceObjects[i].sourcePath, U" ID:", sourceObjects[i].identityChecksum, U" with ", compilerFlags, U"."));
 			script_printMessage(output, language, string_combine(U"Compiling ", sourceObjects[i].sourcePath, U" ID:", sourceObjects[i].identityChecksum, U" with ", compilerFlags, U"."));
 			string_append(output, compilerName, compilerFlags, U" -c ", sourceObjects[i].sourcePath, U" -o ", sourceObjects[i].objectPath, U"\n");
 			string_append(output, compilerName, compilerFlags, U" -c ", sourceObjects[i].sourcePath, U" -o ", sourceObjects[i].objectPath, U"\n");
+			if (language == ScriptLanguage::Batch) {
+				string_append(output,  ")\n");
+			} else if (language == ScriptLanguage::Bash) {
+				string_append(output, U"fi\n");
+			}
+			// Remember each object name for linking.
 			string_append(allObjects, U" ", sourceObjects[i].objectPath);
 			string_append(allObjects, U" ", sourceObjects[i].objectPath);
 		}
 		}
 		script_printMessage(output, language, string_combine(U"Linking with ", linkerFlags, U"."));
 		script_printMessage(output, language, string_combine(U"Linking with ", linkerFlags, U"."));

+ 9 - 81
Source/tools/wizard/main.cpp

@@ -1,10 +1,8 @@
 
 
 // TODO:
 // TODO:
-// * Make this project into an application that starts automatically to test GUI and sound after building the Builder build system.
-// * Let the user browse a file system and select a location for a new or existing project.
-// * Explain how everything works when starting for the first time, using a command line argument.
 // * A catalogue of SDK examples with images and descriptions loaded automatically from their folder.
 // * A catalogue of SDK examples with images and descriptions loaded automatically from their folder.
 //     * Offer one-click build and execution of SDK examples on multiple platforms, while explaining how the building works.
 //     * Offer one-click build and execution of SDK examples on multiple platforms, while explaining how the building works.
+// * Let the user browse a file system and select a location for a new or existing project.
 
 
 #include "../../DFPSR/includeFramework.h"
 #include "../../DFPSR/includeFramework.h"
 #include "sound.h"
 #include "sound.h"
@@ -22,60 +20,24 @@ UR"QUOTE(
 Begin : Panel
 Begin : Panel
 	Name = "mainPanel"
 	Name = "mainPanel"
 	Solid = 0
 	Solid = 0
-	Begin : Panel
-		Name = "toolPanel"
-		Color = 180,180,180
-		Solid = 1
-		bottom = 50
-	End
+	Color = 180,180,180
 End
 End
 )QUOTE";
 )QUOTE";
 
 
-static const double pi = 3.1415926535897932384626433832795;
-static const double cyclesToRadians = pi * 2.0;
-static const int toneCount = 9;
-int basicTone, boomSound;
-int playing[toneCount];
-void createTestProject() {
-	for (int t = 0; t < toneCount; t++) {
-		playing[t] = -1;
-	}
-	// Pure tone
-	basicTone = generateMonoSoundBuffer(U"sine", 441, 44100, soundFormat_F32, [](double time) -> double {
-		return sin(time * (cyclesToRadians * 100));
-	});
-	// Loaded from file
-	boomSound = loadSoundFromFile(file_combinePaths(file_getApplicationFolder(), U"Boom.wav"));
-}
-
-static EnvelopeSettings envelope = EnvelopeSettings(0.1, 0.2, 0.8, 0.4, 0.1, -0.02, 0.04, 0.5);
-static double previewPressTime = 1.0;
-static double previewViewTime = 4.0;
+int boomSound;
 
 
 DSR_MAIN_CALLER(dsrMain)
 DSR_MAIN_CALLER(dsrMain)
 void dsrMain(List<String> args) {
 void dsrMain(List<String> args) {
-	printText(U"Input arguments:\n");
-	for (int a = 0; a < args.length(); a++) {
-		printText(U"  args[", a, "] = ", args[a], U"\n");
-	}
-
-	// Start sound thread
-	printText(U"Initializing sound\n");
+	// Start sound
 	sound_initialize();
 	sound_initialize();
-
-	// Create something to test
-	printText(U"Creating test project\n");
-	createTestProject();
+	boomSound = loadSoundFromFile(file_combinePaths(file_getApplicationFolder(), U"Boom.wav"));
 
 
 	// Create a window
 	// Create a window
-	window = window_create(U"Sound generator", 800, 600);
-
-	// Load an interface to the window
+	window = window_create(U"DFPSR wizard application", 800, 600);
 	window_loadInterfaceFromString(window, interfaceContent);
 	window_loadInterfaceFromString(window, interfaceContent);
 
 
 	// Find components
 	// Find components
 	mainPanel = window_findComponentByName(window, U"mainPanel");
 	mainPanel = window_findComponentByName(window, U"mainPanel");
-	toolPanel = window_findComponentByName(window, U"toolPanel");
 
 
 	// Bind methods to events
 	// Bind methods to events
 	window_setKeyboardEvent(window, [](const KeyboardEvent& event) {
 	window_setKeyboardEvent(window, [](const KeyboardEvent& event) {
@@ -83,45 +45,15 @@ void dsrMain(List<String> args) {
 		if (event.keyboardEventType == KeyboardEventType::KeyDown) {
 		if (event.keyboardEventType == KeyboardEventType::KeyDown) {
 			if (key == DsrKey_Escape) {
 			if (key == DsrKey_Escape) {
 				running = false;
 				running = false;
-			} else if (key >= DsrKey_1 && key <= DsrKey_9) {
-				int toneIndex = key - DsrKey_1;
-				printText(U"Start tone ", toneIndex, U"\n");
-				playing[toneIndex] = playSound(basicTone, true, 0.25, 0.25, 3.0 + toneIndex * 0.25, envelope);
-			} else if (key == DsrKey_0) {
-				playSound(boomSound, false, 0.25, 1.0, 1.0);
-			}
-		} else if (event.keyboardEventType == KeyboardEventType::KeyUp) {
-			if (key >= DsrKey_1 && key <= DsrKey_9) {
-				int toneIndex = key - DsrKey_1;
-				printText(U"End tone ", toneIndex, U"\n");
-				releaseSound(playing[toneIndex]); // Soft stop with following release
-			} else if (key == DsrKey_Space) {
-				stopAllSounds();
 			}
 			}
-		} else if (event.keyboardEventType == KeyboardEventType::KeyType) {
-			String message;
-			string_append(message, U"Typed ");
-			string_appendChar(message, event.character);
-			string_append(message, " of code ", event.character, "\n");
-			printText(message);
 		}
 		}
 	});
 	});
-	/*
-	component_setMouseDownEvent(mainPanel, [](const MouseEvent& event) {
-		
-	});
-	component_setMouseMoveEvent(mainPanel, [](const MouseEvent& event) {
-		
-	});
-	component_setMouseUpEvent(mainPanel, [](const MouseEvent& event) {
-		
-	});
-	*/
 	window_setCloseEvent(window, []() {
 	window_setCloseEvent(window, []() {
 		running = false;
 		running = false;
 	});
 	});
 
 
 	// Execute
 	// Execute
+	playSound(boomSound, false, 1.0, 1.0, 0.25); // TODO: Get the initial sound to play.
 	while(running) {
 	while(running) {
 		// Wait for actions so that we don't render until an action has been recieved
 		// Wait for actions so that we don't render until an action has been recieved
 		// This will save battery on laptops for applications that don't require animation
 		// This will save battery on laptops for applications that don't require animation
@@ -131,16 +63,12 @@ void dsrMain(List<String> args) {
 		// Fill the background
 		// Fill the background
 		AlignedImageRgbaU8 canvas = window_getCanvas(window);
 		AlignedImageRgbaU8 canvas = window_getCanvas(window);
 		image_fill(canvas, ColorRgbaI32(64, 64, 64, 255));
 		image_fill(canvas, ColorRgbaI32(64, 64, 64, 255));
-		// Draw things
-		drawEnvelope(canvas, IRect(0, 50, 550, 100), envelope, previewPressTime, previewViewTime);
-		drawSound(canvas, IRect(0, 150, 550, 100), boomSound);
-		drawSound(canvas, IRect(0, 250, 550, 100), basicTone);
 		// Draw interface
 		// Draw interface
 		window_drawComponents(window);
 		window_drawComponents(window);
 		// Show the final image
 		// Show the final image
 		window_showCanvas(window);
 		window_showCanvas(window);
 	}
 	}
-	// Close sound thread
-	printText(U"Terminating sound\n");
+
+	// Close sound
 	sound_terminate();
 	sound_terminate();
 }
 }

+ 0 - 58
Source/tools/wizard/sound.cpp

@@ -373,64 +373,6 @@ void stopAllSounds() {
 	soundMutex.unlock();
 	soundMutex.unlock();
 }
 }
 
 
-void drawEnvelope(ImageRgbaU8 target, const IRect &region, const EnvelopeSettings &envelopeSettings, double releaseTime, double viewTime) {
-	int top = region.top();
-	int bottom = region.bottom() - 1;
-	Envelope envelope = Envelope(envelopeSettings);
-	double secondsPerPixel = viewTime / region.width();
-	draw_rectangle(target, region, ColorRgbaI32(0, 0, 0, 255));
-	draw_rectangle(target, IRect(region.left(), region.top(), region.width() * (releaseTime / viewTime), region.height() / 8), ColorRgbaI32(0, 128, 128, 255));
-	int oldHardY = bottom;
-	for (int s = 0; s < region.width(); s++) {
-		int x = s + region.left();
-		double time = s * secondsPerPixel;
-		double smoothLevel = envelope.getVolume(time < releaseTime, secondsPerPixel);
-		double hardLevel = envelope.currentGoal;
-		if (envelope.done()) {
-			draw_line(target, x, top, x, (top * 7 + bottom) / 8, ColorRgbaI32(128, 0, 0, 255));
-		} else {
-			draw_line(target, x, (top * smoothLevel) + (bottom * (1.0 - smoothLevel)), x, bottom, ColorRgbaI32(64, 64, 0, 255));
-			int hardY = (top * hardLevel) + (bottom * (1.0 - hardLevel));
-			draw_line(target, x, oldHardY, x, hardY, ColorRgbaI32(255, 255, 255, 255));
-			oldHardY = hardY;
-		}
-	}
-}
-
-void drawSound(dsr::ImageRgbaU8 target, const dsr::IRect &region, int soundIndex) {
-	draw_rectangle(target, region, ColorRgbaI32(128, 128, 128, 255));
-	Sound *sound = &(sounds[soundIndex]);
-	int innerHeight = region.height() / sound->channelCount;
-	for (int c = 0; c < sound->channelCount; c++) {
-		IRect innerBound = IRect(region.left() + 1, region.top() + 1, region.width() - 2, innerHeight - 2);
-		draw_rectangle(target, innerBound, ColorRgbaI32(0, 0, 0, 255));
-		double strideX = ((double)sound->sampleCount - 1.0) / (double)innerBound.width();	
-		double scale = innerBound.height() * 0.5;
-		double center = innerBound.top() + scale;
-		draw_line(target, innerBound.left(), center, innerBound.right() - 1, center, ColorRgbaI32(0, 0, 255, 255));
-		if (strideX > 1.0) {
-			double startSample = 0.0;
-			double endSample = strideX;
-			for (int x = innerBound.left(); x < innerBound.right(); x++) {
-				float minimum = 1.0, maximum = -1.0;
-				// TODO: Switch between min-max sampling (denser) and linear interpolation (sparser)
-				sound->sampleMinMax(minimum, maximum, (int)startSample, (int)endSample, c);
-				draw_line(target, x, center - (minimum * scale), x, center - (maximum * scale), ColorRgbaI32(255, 255, 255, 255));
-				startSample = endSample;
-				endSample = endSample + strideX;
-			}
-		} else {
-			double sampleX = 0.0;
-			for (int x = innerBound.left(); x < innerBound.right(); x++) {
-				float valueLeft = sound->sampleLinear_clamped(sampleX, c);
-				sampleX += strideX;
-				float valueRight = sound->sampleLinear_clamped(sampleX, c);
-				draw_line(target, x, center - (valueLeft * scale), x, center - (valueRight * scale), ColorRgbaI32(255, 255, 255, 255));
-			}
-		}
-	}
-}
-
 #define PREPARE_SAMPLE \
 #define PREPARE_SAMPLE \
 	double envelope = player->envelope.getVolume(player->sustained, outputSoundStep);
 	double envelope = player->envelope.getVolume(player->sustained, outputSoundStep);
 #define NEXT_SAMPLE_CYCLIC \
 #define NEXT_SAMPLE_CYCLIC \

+ 0 - 4
Source/tools/wizard/sound.h

@@ -36,8 +36,4 @@ void stopSound(int64_t playerID);
 // Stop all sounds at once
 // Stop all sounds at once
 void stopAllSounds();
 void stopAllSounds();
 
 
-// Visualization
-void drawEnvelope(dsr::ImageRgbaU8 target, const dsr::IRect &region, const EnvelopeSettings &envelopeSettings, double releaseTime, double viewTime);
-void drawSound(dsr::ImageRgbaU8 target, const dsr::IRect &region, int soundIndex);
-
 #endif
 #endif